@cloudgrid-io/mcp 0.20.48 → 0.20.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.20.48",
3
+ "version": "0.20.49",
4
4
  "description": "MCP access to CloudGrid — the live runtime environment where the user's apps run WITH the infrastructure they need out of the box: managed MongoDB, Redis (cache/queues/pub-sub), a pgvector store, persistent disk, and built-in AI with no API keys, in any language or stack, no Docker or Kubernetes to manage. Brainstorm, build, plug, and improve apps, games, dashboards, and tools; instant single-page publishes and full runtime-app deploys with a live URL — for developers and non-technical users. Three editions: local stdio (full toolset), hosted web (light, CLI-free), and a public docs-search server.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -90,7 +90,7 @@ grid grid
90
90
  | `logs [name]` | Stream entity logs |
91
91
  | `open [name]` | Open the entity URL in the browser |
92
92
  | `info [name]` / `builds [name]` | Entity metadata; recent deploys |
93
- | `visibility set <slug> <mode>` | private \| authenticated \| org \| link |
93
+ | `visibility set <slug> <mode>` | private \| grid \| link — or the axes: `--inside <private\|spaces\|grid> --outside <none\|link\|public>` (+ `--require-signin`, `--space <slug>`) |
94
94
  | `rollback` / `versions` | Roll back to a prior deploy; list/tag minted versions |
95
95
  | `env` / `secrets` | Manage runtime env vars and secrets (secret values are never printed) |
96
96
  | `grid` / `list` | List what's on your hub / org |
@@ -7,7 +7,7 @@ Operating rules:
7
7
  3b. No grid yet (first-time signed-in user): grid_plug returns needs_grid_create when the account has no grid, and a plug can fail with 403 NO_ACTIVE_ORG. Do NOT send the user to the console — suggest a short slug (3-40 lowercase letters/digits/hyphens, starting with a letter), confirm it with the user (it is permanent and appears in URLs), call grid_create_grid, then re-call grid_plug with grid: <slug>.
8
8
  3c. A failed runtime build never loses the work: the source was uploaded before the build ran and lives on the entity. Read the build log (grid_check_deploy) and fix; if it cannot be fixed from this session (hosted, no filesystem), hand the user their files — grid_get_app_source returns a source zip link (source_download_url), and `npx -y @cloudgrid-io/cli@latest pull <grid>/<slug>` downloads the project AND links the folder so the next plug updates the SAME entity locally. Offer one of these instead of abandoning the build or starting over.
9
9
  4. Load specifics on demand. Use grid_get_template({kind, name}) to pull the exact workflow, template, or example you need (kind ∈ workflow|template|example|rule|troubleshooting|doc).
10
- 5. Always return the live share URL at the end — that is the deliverable. On a NEW plug, then ASK the user who should be able to open it — private (only you), a space, your grid, anyone with the link, or anyone signed in — and set their choice with grid_visibility. Never pick the visibility silently. On a re-plug/edit of an existing entity, leave its current visibility as-is unless the user asks to change it.
10
+ 5. Always return the live share URL at the end — that is the deliverable. On a NEW plug, then ASK the user who should be able to open it — private (only you), your grid, or anyone with the link (optionally sign-in required, or search-indexed for public findability; selected spaces via inside: spaces) — and set their choice with grid_visibility. Never pick the visibility silently. On a re-plug/edit of an existing entity, leave its current visibility as-is unless the user asks to change it. Note the model: visibility is two axes — inside the grid (private | spaces | grid) and outside it (none | link | public); the old 'authenticated' mode is retired (it equals a sign-in-required link) and 'org' is gone (use grid).
11
11
  6. Brainstorm first (lightly) for a real app, then minimize questions. For a substantial runtime app, take ONE lightweight beat before generating/plugging: confirm the goal + 3-5 core features in a sentence, check for a matching template/recipe (capability-map), and infer the data/runtime needs and STATE them ("I'll add a database so entries are saved"). Keep it to a line or two; never ask a non-technical user infra questions they can't answer. A simple single page skips this and builds immediately. Otherwise use sensible defaults and build; don't front-load setup questions.
12
12
  7. If a signed-in publish fails with a server error, do not fall back to anonymous publishing (it burns the anonymous quota and downgrades ownership); surface the error, use the CLI fallback if offered, or ask the user.
13
13
  8. When signed in and the user has more than one grid, do not assume a target. For a NEW app, establish the grid UP FRONT — grid_start returns the grids; if there is more than one, ask "which grid should this go on?" before you start building, and carry that slug into grid_plug. As a backstop, grid_plug hard-gates this: an authed create with >1 grid and no chosen grid returns needs_grid (the picker) instead of plugging; a create with no auth and no anon returns needs_auth (sign-in vs anonymous) instead of silently publishing anonymously. Relay either ask to the user; never pick silently.
@@ -40,11 +40,17 @@ The command prints the outlet URL. Hand that URL to the user.
40
40
 
41
41
  `grid visibility set <slug> <mode>` accepts:
42
42
 
43
- - `link` — anyone with the URL can open it. Use this for sharing.
43
+ - `link` — anyone with the URL can open it. Use this for sharing. Add
44
+ `--require-signin` for signed-in accounts only, `--indexed` to be findable by
45
+ search engines.
44
46
  - `private` — only the owner.
45
- - `authenticated` — any logged-in user.
46
- - `org` — members of the org.
47
- - `space` — members of the entity's space.
47
+ - `grid` — everyone in the grid.
48
+
49
+ Finer control uses the two axes instead of a mode:
50
+ `grid visibility set <slug> --inside <private|spaces|grid> --outside <none|link|public> [--space <slug>...] [--require-signin]`
51
+ (`--outside public` = anyone, search-indexed. `authenticated` is retired — it
52
+ equals `--inside private --outside link --require-signin`; `org` is gone — use
53
+ `grid`.)
48
54
 
49
55
  Default to `link` when the user says "share" or "make it public". Confirm before
50
56
  using a wider mode than they asked for.
@@ -29,12 +29,20 @@ export const ANON_HTML_MAX_BYTES = 2_000_000;
29
29
  export const AUTHED_HTML_MAX_BYTES = 25_000_000;
30
30
  export const CONSOLE_URL = "https://console.cloudgrid.io/";
31
31
 
32
+ // Display labels for visibility values (two-axis model). The OPTIONS list is what
33
+ // the post-plug ask offers; the LABELS map also keeps legacy keys (org,
34
+ // authenticated, space) so an entity's CURRENT stored value still renders — do
35
+ // not offer those as choices.
36
+ export const VISIBILITY_OPTIONS = ["private", "grid", "link"];
32
37
  export const VISIBILITY_LABELS = {
33
38
  private: "Only you",
34
- org: "Your grid",
35
- authenticated: "Anyone signed in",
36
- space: "A space",
39
+ grid: "Everyone in your grid",
37
40
  link: "Anyone with the link",
41
+ // display-only (legacy stored values / axis renderings):
42
+ org: "Everyone in your grid",
43
+ authenticated: "Anyone with the link, sign-in required",
44
+ space: "Selected spaces",
45
+ public: "Anyone, findable by search",
38
46
  };
39
47
 
40
48
  // ── Widget resources (ChatGPT Apps SDK, web edition only) ────────────────────
@@ -13,6 +13,7 @@ import {
13
13
  AUTHED_HTML_MAX_BYTES,
14
14
  CONSOLE_URL,
15
15
  VISIBILITY_LABELS,
16
+ VISIBILITY_OPTIONS,
16
17
  APPS_WIDGETS_ENABLED,
17
18
  GRID_PICKER_URI,
18
19
  PLUG_UPLOAD_TIMEOUT_MS,
@@ -316,8 +317,8 @@ const GUEST_ORG_SLUG = "guest";
316
317
  // `<slug>--<grid>.cloudgrid.io`), so it must never be the primary source:
317
318
  // - inspiration (HTML drops): path-based at the org apex
318
319
  // https://<grid>.cloudgrid.io/<slug>
319
- // - runtime (app/agent): flat-arch double-dash host
320
- // https://<slug>--<grid>.cloudgrid.io
320
+ // - runtime (app/agent): subdomain
321
+ // https://<slug>.<grid>.cloudgrid.io
321
322
  // Anonymous drops are grid-less in the response (`grid: null`); they live under
322
323
  // the Guest Org, so the apex slug is the constant `guest`.
323
324
  function composePlugUrl(data) {
@@ -326,11 +327,7 @@ function composePlugUrl(data) {
326
327
  const grid = data?.grid || GUEST_ORG_SLUG;
327
328
  const kind = data?.detection?.kind;
328
329
  if (kind === "app" || kind === "agent") {
329
- // Flat-arch host: runtime entities serve at `<slug>--<grid>.cloudgrid.io`
330
- // (double-dash), NOT the legacy nested `<slug>.<grid>` form — the latter
331
- // 404s on every current grid. Still fallback-only; the server's `data.url`
332
- // remains the primary source (resolvePlugUrl).
333
- return `https://${slug}--${grid}.cloudgrid.io`;
330
+ return `https://${slug}.${grid}.cloudgrid.io`;
334
331
  }
335
332
  // inspiration (and any unknown/static kind) — path-based at the org apex.
336
333
  return `https://${grid}.cloudgrid.io/${slug}`;
@@ -1750,14 +1747,14 @@ export async function runPlug(ctx, input, deps = {}) {
1750
1747
  const current = typeof data.visibility === "string" ? data.visibility : null;
1751
1748
  structured.console_url = CONSOLE_URL;
1752
1749
  if (current) structured.current_visibility = current;
1753
- structured.visibility_options = Object.entries(VISIBILITY_LABELS).map(([v, l]) => ({ value: v, label: l }));
1750
+ structured.visibility_options = VISIBILITY_OPTIONS.map((v) => ({ value: v, label: VISIBILITY_LABELS[v] }));
1754
1751
  lines.push(`Manage all your apps in your grid: ${CONSOLE_URL}`);
1755
1752
  lines.push(
1756
1753
  `Now ASK the user who should be able to open this${current ? ` (currently ${VISIBILITY_LABELS[current] ?? current})` : ""}, then set their choice with grid_visibility — do not decide it for them. Options: ${
1757
- Object.entries(VISIBILITY_LABELS)
1758
- .map(([v, l]) => `${v} (${l})`)
1754
+ VISIBILITY_OPTIONS
1755
+ .map((v) => `${v} (${VISIBILITY_LABELS[v]})`)
1759
1756
  .join("; ")
1760
- }.`,
1757
+ }. Finer control: a sign-in-required link (require_signin), search-indexed (indexed), or selected spaces (inside: spaces).`,
1761
1758
  );
1762
1759
  }
1763
1760
  return { text: lines.join("\n"), structured };
@@ -1848,7 +1845,7 @@ export async function runPickup(ctx, { id, into_org_slug, name, source_version_i
1848
1845
  // direct API — works on the hosted edition where the CLI-wrapping share tool is
1849
1846
  // unavailable. Kind-aware routing (see below). Defaults to the drop made in this
1850
1847
  // session, so "make it private" needs no ids.
1851
- export async function runVisibility(ctx, { target, visibility, kind, org }) {
1848
+ export async function runVisibility(ctx, { target, visibility, inside, outside, require_signin, spaces, indexed, kind, org }) {
1852
1849
  const token = await ctx.getToken();
1853
1850
  if (!token) {
1854
1851
  throw new Error("Changing visibility needs an owner. Run grid_login first.");
@@ -1865,32 +1862,78 @@ export async function runVisibility(ctx, { target, visibility, kind, org }) {
1865
1862
  headers["X-CloudGrid-Org"] = orgSlug;
1866
1863
  }
1867
1864
 
1868
- // Two visibility surfaces with two vocabularies:
1869
- // - inspirations: PATCH /api/v2/inspirations/:id { private|space|authenticated|org|link }
1870
- // - runtimes: PATCH /api/v2/entities/:id/visibility { private|authenticated|grid|link }
1871
- // Whole-grid visibility is the legacy `org` on inspirations and the canonical
1872
- // `grid` on runtimes. Normalize the caller's value to `grid`, then map per
1873
- // route. `space` exists only on inspirations.
1874
- const vis = visibility === "org" ? "grid" : visibility;
1875
- const runtimeValue = vis; // private | authenticated | grid | link
1876
- const inspirationValue = vis === "grid" ? "org" : vis; // private | space | authenticated | org | link
1865
+ // ── Two-axis visibility model ──────────────────────────────────────
1866
+ // A scope is two independent axes:
1867
+ // inside (share_scope): private | spaces | grid — who in the grid sees it
1868
+ // outside (external_access): none | link | public — reach beyond the grid
1869
+ // require_signin: link only — the link needs a signed-in CloudGrid account
1870
+ // Legacy positional modes (private|grid|link) still ride the { visibility }
1871
+ // body; `authenticated` is RETIRED as a first-class mode and maps to the axis
1872
+ // body it equals (private + link + require_signin); `org` is rejected;
1873
+ // `public` as a mode is an alias of `link` (the axis value `public` is the
1874
+ // indexed one); `space` maps to inside: spaces and needs the `spaces` list.
1875
+ const normSpaces = [...new Set((spaces || []).map((s) => String(s).trim().toLowerCase()).filter(Boolean))];
1876
+ const useAxes = inside !== undefined || outside !== undefined;
1877
+ let body;
1878
+ let requested; // for the result text when the response is sparse
1879
+ if (useAxes) {
1880
+ if (visibility) throw new Error("Pass either `visibility` or the `inside`/`outside` axes, not both.");
1881
+ if (!inside || !outside) {
1882
+ throw new Error("Both axes are required: inside (private|spaces|grid) and outside (none|link|public).");
1883
+ }
1884
+ if (inside === "spaces" && normSpaces.length === 0) {
1885
+ throw new Error("inside: spaces needs at least one space slug in `spaces`.");
1886
+ }
1887
+ if (inside !== "spaces" && normSpaces.length > 0) {
1888
+ throw new Error("`spaces` only applies with inside: spaces.");
1889
+ }
1890
+ if (require_signin === true && outside !== "link") {
1891
+ throw new Error("require_signin only applies when outside is link.");
1892
+ }
1893
+ body = {
1894
+ share_scope: inside,
1895
+ external_access: outside,
1896
+ ...(require_signin === true ? { require_signin: true } : {}),
1897
+ ...(inside === "spaces" ? { visibility_spaces: normSpaces } : {}),
1898
+ };
1899
+ requested = `inside: ${inside}${inside === "spaces" ? ` (${normSpaces.join(", ")})` : ""}, outside: ${outside}${require_signin === true ? " (sign-in required)" : ""}`;
1900
+ } else {
1901
+ let mode = String(visibility || "").trim().toLowerCase();
1902
+ if (!mode) throw new Error("Pass a visibility mode (private | grid | link), or the inside/outside axes.");
1903
+ if (mode === "org") {
1904
+ throw new Error("'org' visibility is deprecated. Use 'grid' (everyone in the grid).");
1905
+ }
1906
+ if (mode === "public") mode = "link"; // alias; `indexed: true` governs search-indexing
1907
+ if (mode === "authenticated") {
1908
+ // Retired as a first-class mode — the axis body it equals.
1909
+ body = { share_scope: "private", external_access: "link", require_signin: true };
1910
+ requested = "link with sign-in required (the retired 'authenticated')";
1911
+ } else if (mode === "space") {
1912
+ if (normSpaces.length === 0) {
1913
+ throw new Error("'space' visibility needs the `spaces` list (which space slugs can see it) — or use inside: spaces with outside: none.");
1914
+ }
1915
+ body = { share_scope: "spaces", external_access: "none", visibility_spaces: normSpaces };
1916
+ requested = `selected spaces (${normSpaces.join(", ")})`;
1917
+ } else if (mode === "private" || mode === "grid" || mode === "link") {
1918
+ body = { visibility: mode };
1919
+ if (mode === "grid" && normSpaces.length > 0) body.visibility_spaces = normSpaces;
1920
+ if (mode === "link") body.link_indexed = indexed === true;
1921
+ requested = mode + (mode === "link" && indexed === true ? " (search-indexed)" : "");
1922
+ } else {
1923
+ throw new Error(`Unknown visibility '${visibility}'. Use private | grid | link (public = link alias), or the inside/outside axes.`);
1924
+ }
1925
+ }
1877
1926
 
1878
1927
  const k = kind || ctx.state.lastDrop?.kind || null;
1879
1928
  const isRuntimeKind = k === "app" || k === "agent";
1880
1929
 
1881
- if (vis === "space" && isRuntimeKind) {
1882
- throw new Error(
1883
- "'space' visibility isn't available for runtime apps/agents. Use grid, authenticated, private, or link.",
1884
- );
1885
- }
1886
-
1887
- const patch = async (pathName, value) => {
1930
+ const patch = async (pathName) => {
1888
1931
  let res;
1889
1932
  try {
1890
1933
  res = await fetch(`${API_BASE}${pathName}`, {
1891
1934
  method: "PATCH",
1892
1935
  headers,
1893
- body: JSON.stringify({ visibility: value }),
1936
+ body: JSON.stringify(body),
1894
1937
  });
1895
1938
  } catch (err) {
1896
1939
  throw new Error(`Could not reach CloudGrid at ${API_BASE}: ${err.message}`);
@@ -1901,33 +1944,53 @@ export async function runVisibility(ctx, { target, visibility, kind, org }) {
1901
1944
  return { res, data, raw };
1902
1945
  };
1903
1946
 
1947
+ // BOTH realms now take the same realm-scoped visibility PATCH with the
1948
+ // same body vocabulary: /api/v2/entities/:id/visibility (runtimes) and
1949
+ // /api/v2/inspirations/:id/visibility (inspirations).
1904
1950
  const runtimePath = `/api/v2/entities/${encodeURIComponent(id)}/visibility`;
1905
- const inspirationPath = `/api/v2/inspirations/${encodeURIComponent(id)}`;
1951
+ const inspirationPath = `/api/v2/inspirations/${encodeURIComponent(id)}/visibility`;
1906
1952
 
1907
- // Route order. `space` is inspiration-only; a known runtime kind goes straight
1908
- // to the entities route; a known inspiration goes straight to the inspiration
1909
- // route; an unknown kind tries the runtime route first and falls back to the
1910
- // inspiration route on a not-found (mirrors runPickup).
1953
+ // Route order: a known runtime kind goes straight to the entities route; a
1954
+ // known inspiration to the inspiration route; unknown tries runtime first and
1955
+ // falls back on a not-found (mirrors runPickup).
1911
1956
  let order;
1912
- if (vis === "space") order = ["inspiration"];
1913
- else if (isRuntimeKind) order = ["runtime"];
1957
+ if (isRuntimeKind) order = ["runtime"];
1914
1958
  else if (k === "inspiration") order = ["inspiration"];
1915
1959
  else order = ["runtime", "inspiration"];
1916
1960
 
1917
1961
  let last;
1918
1962
  for (let i = 0; i < order.length; i++) {
1919
1963
  const route = order[i];
1920
- last = route === "runtime"
1921
- ? await patch(runtimePath, runtimeValue)
1922
- : await patch(inspirationPath, inspirationValue);
1964
+ last = await patch(route === "runtime" ? runtimePath : inspirationPath);
1923
1965
  if (last.res.ok) {
1924
- const lines = [`Visibility is now ${vis}.`];
1925
- if (last.data?.url) lines.push(last.data.url);
1966
+ const d = last.data || {};
1967
+ // Prefer the server's stored axes (stored axes are authoritative on
1968
+ // the wire); fall back to what we asked for.
1969
+ const lines = [];
1970
+ if (d.share_scope || d.external_access) {
1971
+ const insideLine =
1972
+ d.share_scope === "private" ? "only the owner" :
1973
+ d.share_scope === "spaces" ? `selected spaces${Array.isArray(d.visibility_spaces) && d.visibility_spaces.length ? ` (${d.visibility_spaces.join(", ")})` : ""}` :
1974
+ d.share_scope === "grid" ? "everyone in the grid" : d.share_scope;
1975
+ const outsideLine =
1976
+ d.external_access === "none" ? "no one outside the grid" :
1977
+ d.external_access === "link" ? `anyone with the link${d.require_signin ? " (signed-in accounts only)" : ""}${d.link_indexed ? ", search-indexed" : ""}` :
1978
+ d.external_access === "public" ? "anyone, findable by search engines" : d.external_access;
1979
+ lines.push(`Visibility set — inside the grid: ${insideLine}; outside: ${outsideLine}.`);
1980
+ } else {
1981
+ lines.push(`Visibility is now ${d.visibility || requested}.`);
1982
+ }
1983
+ if (d.url) lines.push(d.url);
1926
1984
  return {
1927
1985
  text: lines.join("\n"),
1928
1986
  structured: {
1929
- visibility: vis,
1930
- ...(last.data?.url ? { url: last.data.url } : {}),
1987
+ visibility: d.visibility || (useAxes ? undefined : String(visibility || "").trim().toLowerCase() || undefined),
1988
+ ...(d.share_scope ? { share_scope: d.share_scope } : {}),
1989
+ ...(d.external_access ? { external_access: d.external_access } : {}),
1990
+ ...(d.require_signin !== undefined ? { require_signin: d.require_signin === true } : {}),
1991
+ ...(Array.isArray(d.visibility_spaces) ? { visibility_spaces: d.visibility_spaces } : {}),
1992
+ ...(d.link_indexed !== undefined ? { link_indexed: d.link_indexed === true } : {}),
1993
+ ...(d.url ? { url: d.url } : {}),
1931
1994
  },
1932
1995
  };
1933
1996
  }
@@ -659,15 +659,25 @@ export function registerTools(server, ctx) {
659
659
  reg(
660
660
  "grid_visibility",
661
661
  {
662
- description: "Change who can see a CloudGrid inspiration OR runtime app/agent: private, authenticated, grid (everyone in the grid), or link (anyone with the URL). `space` is inspiration-only. Use when the user wants to make something private, restrict who sees it, or open it up — including right after a drop, with no target id needed. Kind-aware: routes runtime apps/agents to the entities visibility surface and inspirations to the inspiration surface automatically. Defaults to the drop made in this session. Requires sign-in. Calls the API directly.",
662
+ description: "Change who can see a CloudGrid inspiration OR runtime app/agent. Simple modes: private (only the user), grid (everyone in their grid), link (anyone with the URL — add indexed: true to be findable by search engines). Finer control via the TWO AXES instead: inside (who in the grid: private | spaces | grid) and outside (reach beyond it: none | link | public), with require_signin for a members-only link and `spaces` for selected spaces. 'authenticated' is retired (it maps to a sign-in-required link); 'org' is gone — use grid. Use when the user wants to make something private, restrict who sees it, or open it up — including right after a drop, with no target id needed. Kind-aware routing across both surfaces. Defaults to the drop made in this session. Requires sign-in. Calls the API directly.",
663
663
  inputSchema: {
664
- visibility: z.enum(["private", "space", "authenticated", "grid", "org", "link"]).describe("The new scope. Use `grid` for whole-grid visibility (`org` is the deprecated alias). `space` is inspiration-only."),
664
+ visibility: z.enum(["private", "grid", "link", "public", "authenticated", "space"]).optional().describe("Simple mode: private | grid | link ('public' is an alias of link — pass indexed: true for search-findable; 'authenticated' is the retired alias for a sign-in-required link; 'space' needs the `spaces` list). Pass either this OR the inside/outside axes."),
665
+ inside: z.enum(["private", "spaces", "grid"]).optional().describe("Axis 1 — who in the grid can see it: private (only the user), spaces (selected spaces — pass `spaces`), or grid (everyone in the grid). Use together with `outside`, instead of `visibility`."),
666
+ outside: z.enum(["none", "link", "public"]).optional().describe("Axis 2 — reach beyond the grid: none, link (anyone with the link; add require_signin: true for signed-in accounts only), or public (anyone, and findable by search engines)."),
667
+ require_signin: z.boolean().optional().describe("With outside: link (or visibility: link) — the link requires a signed-in CloudGrid account."),
668
+ spaces: z.array(z.string()).optional().describe("Space slugs, for inside: spaces (or the legacy space/grid modes)."),
669
+ indexed: z.boolean().optional().describe("With visibility: link — make the page search-engine indexable."),
665
670
  target: z.string().optional().describe("Entity id. Defaults to this session's last drop."),
666
671
  kind: z.enum(["inspiration", "app", "agent"]).optional().describe("Entity kind. Omit to auto-detect from this session's last drop (falls back to trying the runtime surface, then the inspiration surface)."),
667
672
  org: z.string().optional().describe("Grid of the entity. Defaults to the active grid."),
668
673
  },
669
674
  outputSchema: {
670
- visibility: z.string().describe("The visibility that was set."),
675
+ visibility: z.string().optional().describe("The legacy mode that was set, when a simple mode was used."),
676
+ share_scope: z.string().optional().describe("Stored axis: who in the grid (private | spaces | grid)."),
677
+ external_access: z.string().optional().describe("Stored axis: reach beyond the grid (none | link | public)."),
678
+ require_signin: z.boolean().optional().describe("Whether the link requires sign-in."),
679
+ visibility_spaces: z.array(z.string()).optional().describe("The space slugs, when share_scope is spaces."),
680
+ link_indexed: z.boolean().optional().describe("Whether the link is search-indexed."),
671
681
  url: z.string().optional().describe("URL of the entity, if returned."),
672
682
  },
673
683
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },