@cloudgrid-io/mcp 0.4.4 → 0.4.5

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 +3 -2
  2. package/src/tools.js +59 -36
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
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": {
@@ -22,7 +22,8 @@
22
22
  "smoke:web": "node test/smoke-web.mjs",
23
23
  "smoke:redrop": "node test/smoke-redrop.mjs",
24
24
  "smoke:oauth": "node test/smoke-oauth.mjs",
25
- "test:auth": "node test/auth.test.mjs"
25
+ "test:auth": "node test/auth.test.mjs",
26
+ "build:mcpb": "node scripts/build-mcpb.mjs"
26
27
  },
27
28
  "dependencies": {
28
29
  "@cloudgrid-io/cli": "^0.9.20",
package/src/tools.js CHANGED
@@ -601,6 +601,7 @@ export function registerTools(server, ctx) {
601
601
  slug: z.string().describe("Org slug to pass as the org parameter."),
602
602
  name: z.string().describe("Human-readable org name."),
603
603
  role: z.string().describe("User's role in the org."),
604
+ is_active: z.boolean().optional().describe("True if this is the user's currently active org."),
604
605
  })).optional().describe("The user's orgs, when org choice is needed."),
605
606
  needs_sign_in: z.boolean().optional().describe("True when sign-in is needed before dropping."),
606
607
  login_url: z.string().optional().describe("Sign-in URL when authentication is needed."),
@@ -634,11 +635,16 @@ export function registerTools(server, ctx) {
634
635
  structured: { needs_sign_in: true, login_url: url },
635
636
  });
636
637
  }
637
- // Stateless org disambiguation — no dependency on prior-call state
638
- // so it works even when the client reconnects on every tool call
639
- // (ChatGPT Apps SDK behaviour).
640
- {
638
+ }
639
+
640
+ // Stateless org disambiguation — both editions, when authenticated.
641
+ // No dependency on prior-call state so it works even when the client
642
+ // reconnects on every tool call (ChatGPT Apps SDK behaviour).
643
+ if (input?.anonymous !== true) {
644
+ const token = await ctx.getToken();
645
+ if (token) {
641
646
  const orgs = await fetchUserOrgs(token);
647
+ const activeOrg = await ctx.getActiveOrg();
642
648
  const suppliedOrg = input?.org;
643
649
  const validOrg = suppliedOrg && orgs.some((o) => o.slug === suppliedOrg);
644
650
  if (validOrg) {
@@ -646,13 +652,22 @@ export function registerTools(server, ctx) {
646
652
  input = { ...(input || {}), org: suppliedOrg };
647
653
  } else if (orgs.length > 1) {
648
654
  // No valid org supplied and multiple orgs — ask once.
655
+ // Mark the active org so the agent can offer it as the default.
656
+ const annotated = orgs.map((o) => ({
657
+ ...o,
658
+ is_active: o.slug === activeOrg,
659
+ }));
660
+ annotated.sort((a, b) => (b.is_active ? 1 : 0) - (a.is_active ? 1 : 0));
649
661
  const lines = ["Which org should this be published to?"];
650
- for (const o of orgs) lines.push(` ${o.slug} — ${o.name} (${o.role})`);
662
+ for (const o of annotated) {
663
+ const tag = o.is_active ? " (your active org)" : "";
664
+ lines.push(` ${o.slug} — ${o.name} (${o.role})${tag}`);
665
+ }
651
666
  lines.push("Pass the org slug in the org parameter to publish.");
652
667
  return okResult({
653
668
  text: lines.join("\n"),
654
- structured: { needs_org: true, orgs },
655
- meta: { "openai/outputTemplate": ORG_PICKER_URI },
669
+ structured: { needs_org: true, orgs: annotated },
670
+ ...(ctx.edition === "web" ? { meta: { "openai/outputTemplate": ORG_PICKER_URI } } : {}),
656
671
  });
657
672
  } else if (orgs.length === 1) {
658
673
  // Single org — publish to it silently.
@@ -791,36 +806,44 @@ export function registerTools(server, ctx) {
791
806
  },
792
807
  );
793
808
 
794
- // Org listing — web edition only (local edition uses cloudgrid_whoami).
795
- if (ctx.edition === "web") {
796
- server.registerTool(
797
- "cloudgrid_orgs",
798
- {
799
- description: "List the signed-in user's organizations. Returns each org's slug, name, and the user's role. Use to discover which org to publish to. Requires sign-in.",
800
- inputSchema: {},
801
- outputSchema: {
802
- orgs: z.array(z.object({
803
- slug: z.string().describe("Org slug."),
804
- name: z.string().describe("Human-readable org name."),
805
- role: z.string().describe("User's role in the org."),
806
- })).describe("The user's org memberships."),
807
- },
808
- annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
809
- },
810
- async () => {
811
- const token = await ctx.getToken();
812
- if (!token) {
813
- return fail("You are not signed in. Run cloudgrid_login first.");
814
- }
815
- const orgs = await fetchUserOrgs(token);
816
- if (orgs.length === 0) {
817
- return okResult({ text: "No organizations found.", structured: { orgs: [] } });
818
- }
819
- const lines = orgs.map((o) => `${o.slug} — ${o.name} (${o.role})`);
820
- return okResult({ text: lines.join("\n"), structured: { orgs } });
809
+ // Org listing — both editions.
810
+ server.registerTool(
811
+ "cloudgrid_orgs",
812
+ {
813
+ description: "List the signed-in user's organizations. Returns each org's slug, name, and the user's role. Use to discover which org to publish to. Requires sign-in.",
814
+ inputSchema: {},
815
+ outputSchema: {
816
+ orgs: z.array(z.object({
817
+ slug: z.string().describe("Org slug."),
818
+ name: z.string().describe("Human-readable org name."),
819
+ role: z.string().describe("User's role in the org."),
820
+ is_active: z.boolean().optional().describe("True if this is the user's currently active org."),
821
+ })).describe("The user's org memberships."),
821
822
  },
822
- );
823
- }
823
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
824
+ },
825
+ async () => {
826
+ const token = await ctx.getToken();
827
+ if (!token) {
828
+ return fail("You are not signed in. Run cloudgrid_login first.");
829
+ }
830
+ const orgs = await fetchUserOrgs(token);
831
+ if (orgs.length === 0) {
832
+ return okResult({ text: "No organizations found.", structured: { orgs: [] } });
833
+ }
834
+ const activeOrg = await ctx.getActiveOrg();
835
+ const annotated = orgs.map((o) => ({
836
+ ...o,
837
+ is_active: o.slug === activeOrg,
838
+ }));
839
+ annotated.sort((a, b) => (b.is_active ? 1 : 0) - (a.is_active ? 1 : 0));
840
+ const lines = annotated.map((o) => {
841
+ const tag = o.is_active ? " (your active org)" : "";
842
+ return `${o.slug} — ${o.name} (${o.role})${tag}`;
843
+ });
844
+ return okResult({ text: lines.join("\n"), structured: { orgs: annotated } });
845
+ },
846
+ );
824
847
 
825
848
  if (ctx.edition !== "local") return; // web edition stops here — no CLI tools
826
849