@cloudgrid-io/mcp 0.4.1 → 0.4.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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/tools.js +19 -27
  3. package/src/web.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
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
@@ -572,36 +572,28 @@ export function registerTools(server, ctx) {
572
572
  structured: { needs_sign_in: true, login_url: url },
573
573
  });
574
574
  }
575
- // Org disambiguation with per-session pick gate: the first drop
576
- // always shows the picker when >1 org (even if the model guessed an
577
- // org). The flag ctx.state.awaitingOrgPick gates: only after the
578
- // picker was shown and the re-call supplies a valid slug do we honor
579
- // it. This prevents the model from silently guessing and skipping
580
- // the user's choice.
575
+ // Stateless org disambiguation no dependency on prior-call state
576
+ // so it works even when the client reconnects on every tool call
577
+ // (ChatGPT Apps SDK behaviour).
581
578
  {
582
579
  const orgs = await fetchUserOrgs(token);
583
- if (orgs.length > 1) {
584
- const suppliedOrg = input?.org;
585
- const validOrg = suppliedOrg && orgs.some((o) => o.slug === suppliedOrg);
586
- if (ctx.state.awaitingOrgPick && validOrg) {
587
- // The picker was shown previously and the user (or widget)
588
- // picked a valid org honor it, clear the flag, and publish.
589
- ctx.state.awaitingOrgPick = false;
590
- input = { ...(input || {}), org: suppliedOrg };
591
- } else {
592
- // First drop (any model-guessed org is ignored) or the
593
- // re-call supplied an invalid slug — show the picker.
594
- ctx.state.awaitingOrgPick = true;
595
- const lines = ["Which org should this be published to?"];
596
- for (const o of orgs) lines.push(` ${o.slug} ${o.name} (${o.role})`);
597
- lines.push("Pass the org slug in the org parameter to publish.");
598
- return okResult({
599
- text: lines.join("\n"),
600
- structured: { needs_org: true, orgs },
601
- meta: { "openai/outputTemplate": ORG_PICKER_URI },
602
- });
603
- }
580
+ const suppliedOrg = input?.org;
581
+ const validOrg = suppliedOrg && orgs.some((o) => o.slug === suppliedOrg);
582
+ if (validOrg) {
583
+ // Supplied org matches a real org slug — publish to it.
584
+ input = { ...(input || {}), org: suppliedOrg };
585
+ } else if (orgs.length > 1) {
586
+ // No valid org supplied and multiple orgs — ask once.
587
+ const lines = ["Which org should this be published to?"];
588
+ for (const o of orgs) lines.push(` ${o.slug} ${o.name} (${o.role})`);
589
+ lines.push("Pass the org slug in the org parameter to publish.");
590
+ return okResult({
591
+ text: lines.join("\n"),
592
+ structured: { needs_org: true, orgs },
593
+ meta: { "openai/outputTemplate": ORG_PICKER_URI },
594
+ });
604
595
  } else if (orgs.length === 1) {
596
+ // Single org — publish to it silently.
605
597
  input = { ...(input || {}), org: orgs[0].slug };
606
598
  }
607
599
  }
package/src/web.js CHANGED
@@ -55,7 +55,7 @@ function makeWebContext(sessionId) {
55
55
  let sessionToken = null;
56
56
  return {
57
57
  edition: "web",
58
- state: { pendingLoginCode: null, lastAnonClaim: null, lastDrop: null, anonCookie: null, awaitingOrgPick: false },
58
+ state: { pendingLoginCode: null, lastAnonClaim: null, lastDrop: null, anonCookie: null },
59
59
  canOpenBrowser: false,
60
60
  // Transport OAuth wins; the in-tool login flow is the fallback.
61
61
  getToken: async () => sessionAuth[sessionId] ?? sessionToken,