@cloudgrid-io/mcp 0.2.8 → 0.3.1
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/README.md +37 -10
- package/package.json +1 -1
- package/src/index.js +4 -1
- package/src/tools.js +223 -1
- package/src/web.js +4 -1
package/README.md
CHANGED
|
@@ -43,30 +43,57 @@ It speaks MCP over stdio. Point any MCP client at the `cloudgrid-mcp` command.
|
|
|
43
43
|
|
|
44
44
|
## Tools
|
|
45
45
|
|
|
46
|
+
### Direct-API tools (both editions)
|
|
47
|
+
|
|
46
48
|
| Tool | Wraps | Notes |
|
|
47
49
|
|---|---|---|
|
|
48
|
-
| `cloudgrid_drop` | `POST /api/v2/drop/auto` | Artifact drop. Anonymous, or owned if signed in.
|
|
50
|
+
| `cloudgrid_drop` | `POST /api/v2/drop/auto` | Artifact drop. Anonymous, or owned if signed in. Direct API. |
|
|
49
51
|
| `cloudgrid_claim` | `POST /api/v2/anon-claim` | Claim an anonymous drop into the signed-in account. Direct API. |
|
|
50
|
-
| `cloudgrid_login` | `GET /auth/login` | Start a CLI-free sign-in; returns a URL to open.
|
|
52
|
+
| `cloudgrid_login` | `GET /auth/login` | Start a CLI-free sign-in; returns a URL to open. Direct API. |
|
|
51
53
|
| `cloudgrid_login_status` | `GET /auth/status` | Finish the sign-in; saves the token to the shared CLI credentials. |
|
|
52
54
|
| `cloudgrid_visibility` | `PATCH /api/v2/inspirations/<id>` | Change who can see a drop (private, space, authenticated, org, link). Needs sign-in. Direct API; also in the web edition. |
|
|
53
|
-
| `cloudgrid_init` | `cloudgrid init` | Register an app or agent; optionally seed a web service. |
|
|
54
|
-
| `cloudgrid_plug` | `cloudgrid plug` | Deploy a directory or URL. |
|
|
55
|
-
| `cloudgrid_logs` | `cloudgrid logs` | Snapshot of recent logs. Does not stream. |
|
|
56
|
-
| `cloudgrid_share` | `cloudgrid visibility set` | Set visibility, default link. |
|
|
57
|
-
| `cloudgrid_feedback` | `cloudgrid feedback list` | Read the org feedback feed. |
|
|
58
|
-
| `cloudgrid_brain` | `cloudgrid brain refresh` | Re-run the Grid Brain hooks. |
|
|
59
55
|
|
|
60
56
|
`cloudgrid_drop`, `cloudgrid_claim`, `cloudgrid_visibility`, and the two
|
|
61
|
-
`cloudgrid_login` tools do not wrap the CLI
|
|
57
|
+
`cloudgrid_login` tools do not wrap the CLI -- they call the API directly, so they
|
|
62
58
|
also work in the web edition where no CLI exists. `cloudgrid_login` writes the same
|
|
63
59
|
`~/.cloudgrid/credentials` the CLI uses, so the two share one identity.
|
|
64
60
|
|
|
61
|
+
### CLI-wrapping tools (local edition only)
|
|
62
|
+
|
|
63
|
+
| Tool | Wraps | Notes |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `cloudgrid_init` | `cloudgrid init` | Register an app or agent; optionally seed a web service. |
|
|
66
|
+
| `cloudgrid_plug` | `cloudgrid plug` | Deploy a directory or URL. |
|
|
67
|
+
| `cloudgrid_logs` | `cloudgrid logs` | Snapshot of recent logs. Does not stream. Read-only. |
|
|
68
|
+
| `cloudgrid_share` | `cloudgrid visibility set` | Set visibility, default link. |
|
|
69
|
+
| `cloudgrid_feedback` | `cloudgrid feedback list` | Read the org feedback feed. Read-only. |
|
|
70
|
+
| `cloudgrid_brain` | `cloudgrid brain refresh` | Re-run the Grid Brain hooks. |
|
|
71
|
+
| `cloudgrid_whoami` | `cloudgrid whoami` | Show the signed-in user and active org. Read-only. |
|
|
72
|
+
| `cloudgrid_use` | `cloudgrid use` | Switch the active org. |
|
|
73
|
+
| `cloudgrid_logout` | `cloudgrid logout` | Sign out and clear local credentials. Destructive. |
|
|
74
|
+
| `cloudgrid_status` | `cloudgrid status` | Org dashboard or entity detail. Read-only. |
|
|
75
|
+
| `cloudgrid_info` | `cloudgrid info` | Entity metadata. Read-only. |
|
|
76
|
+
| `cloudgrid_builds` | `cloudgrid builds` | Recent builds and deploys. Read-only. |
|
|
77
|
+
| `cloudgrid_grid` | `cloudgrid grid` | List entities on the hub. Read-only. |
|
|
78
|
+
| `cloudgrid_rename` | `cloudgrid rename` | Rename an entity. |
|
|
79
|
+
| `cloudgrid_unplug` | `cloudgrid unplug` | Take an entity off the grid. Destructive; requires confirm. |
|
|
80
|
+
| `cloudgrid_delete` | `cloudgrid delete` | Archive and delete an entity. Destructive; requires confirm. |
|
|
81
|
+
| `cloudgrid_rollback` | `cloudgrid rollback` | Rollback to a previous version. |
|
|
82
|
+
| `cloudgrid_versions` | `cloudgrid versions` | List published versions. Read-only. |
|
|
83
|
+
| `cloudgrid_env` | `cloudgrid env` | Get, set, or list environment variables. |
|
|
84
|
+
| `cloudgrid_secrets` | `cloudgrid secrets` | Set or list secret names. Never returns secret values. |
|
|
85
|
+
| `cloudgrid_scaffold` | `cloudgrid scaffold` | Generate starter files. |
|
|
86
|
+
| `cloudgrid_doctor` | `cloudgrid doctor` | Run local diagnostics. Read-only. |
|
|
87
|
+
| `cloudgrid_open` | `cloudgrid open` | Return the public URL. Does not open a browser. Read-only. |
|
|
88
|
+
|
|
65
89
|
`cloudgrid_share` and `cloudgrid_visibility` overlap on purpose: `cloudgrid_share`
|
|
66
90
|
wraps the CLI and defaults to `link`; `cloudgrid_visibility` is direct API, takes an
|
|
67
|
-
explicit scope, and defaults its target to the session's last drop
|
|
91
|
+
explicit scope, and defaults its target to the session's last drop -- it is the one
|
|
68
92
|
the web edition gets.
|
|
69
93
|
|
|
94
|
+
All tools carry MCP annotations (`readOnlyHint`, `destructiveHint`,
|
|
95
|
+
`openWorldHint`) for clients that support them.
|
|
96
|
+
|
|
70
97
|
## Test
|
|
71
98
|
|
|
72
99
|
A smoke test spawns the server with a real MCP client, lists the tools, and calls
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Desktop). Full toolset, including the CLI-wrapping tools. Identity comes from
|
|
6
6
|
// the shared ~/.cloudgrid/credentials file, so it interoperates with the CLI.
|
|
7
7
|
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
8
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
10
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
11
|
import { registerTools } from "./tools.js";
|
|
@@ -15,6 +16,8 @@ import {
|
|
|
15
16
|
credentialsPath,
|
|
16
17
|
} from "./auth.js";
|
|
17
18
|
|
|
19
|
+
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url)));
|
|
20
|
+
|
|
18
21
|
const ctx = {
|
|
19
22
|
edition: "local",
|
|
20
23
|
state: { pendingLoginCode: null, lastAnonClaim: null, lastDrop: null, anonCookie: null },
|
|
@@ -25,7 +28,7 @@ const ctx = {
|
|
|
25
28
|
savedLocationNote: () => `Credentials saved to ${credentialsPath()}.`,
|
|
26
29
|
};
|
|
27
30
|
|
|
28
|
-
const server = new McpServer({ name: "cloudgrid-mcp", version
|
|
31
|
+
const server = new McpServer({ name: "cloudgrid-mcp", version });
|
|
29
32
|
registerTools(server, ctx);
|
|
30
33
|
|
|
31
34
|
const transport = new StdioServerTransport();
|
package/src/tools.js
CHANGED
|
@@ -316,6 +316,8 @@ async function runVisibility(ctx, { target, visibility, org }) {
|
|
|
316
316
|
// Registers the tools onto `server`. ctx.edition decides whether the CLI-wrapping
|
|
317
317
|
// tools are included (they need a local machine).
|
|
318
318
|
export function registerTools(server, ctx) {
|
|
319
|
+
// ── Direct-API tools (both editions) ──────────────────────────────────────
|
|
320
|
+
|
|
319
321
|
// Drop — both editions.
|
|
320
322
|
server.tool(
|
|
321
323
|
"cloudgrid_drop",
|
|
@@ -331,6 +333,7 @@ export function registerTools(server, ctx) {
|
|
|
331
333
|
.optional()
|
|
332
334
|
.describe("Force a new drop even if you already dropped in this session (default: update in place)."),
|
|
333
335
|
},
|
|
336
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
334
337
|
async (input) => {
|
|
335
338
|
try {
|
|
336
339
|
return ok(await runDrop(ctx, input || {}));
|
|
@@ -348,6 +351,7 @@ export function registerTools(server, ctx) {
|
|
|
348
351
|
claim_token: z.string().optional().describe("The claim token from an anonymous drop."),
|
|
349
352
|
claim_url: z.string().optional().describe("The claim_url from an anonymous drop; the token is read from it."),
|
|
350
353
|
},
|
|
354
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
351
355
|
async (input) => {
|
|
352
356
|
try {
|
|
353
357
|
return ok(await runClaim(ctx, input || {}));
|
|
@@ -363,6 +367,7 @@ export function registerTools(server, ctx) {
|
|
|
363
367
|
"cloudgrid_login",
|
|
364
368
|
"Start a CLI-free CloudGrid sign-in. Use when the user wants to log in, sign in, or authenticate, or to claim an anonymous drop. Returns a URL to open in the browser; then call cloudgrid_login_status to finish. Uses CloudGrid's existing OAuth.",
|
|
365
369
|
{},
|
|
370
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
366
371
|
async () => {
|
|
367
372
|
const code = newLoginCode();
|
|
368
373
|
ctx.state.pendingLoginCode = code;
|
|
@@ -381,6 +386,7 @@ export function registerTools(server, ctx) {
|
|
|
381
386
|
{
|
|
382
387
|
code: z.string().optional().describe("The sign-in code. Defaults to the most recent cloudgrid_login."),
|
|
383
388
|
},
|
|
389
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
384
390
|
async (input) => {
|
|
385
391
|
const code = input?.code || ctx.state.pendingLoginCode;
|
|
386
392
|
if (!code) return fail("No sign-in is in progress. Run cloudgrid_login first.");
|
|
@@ -419,6 +425,7 @@ export function registerTools(server, ctx) {
|
|
|
419
425
|
target: z.string().optional().describe("Entity id. Defaults to this session's last drop."),
|
|
420
426
|
org: z.string().optional().describe("Org of the entity. Defaults to the active org."),
|
|
421
427
|
},
|
|
428
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
422
429
|
async (input) => {
|
|
423
430
|
try {
|
|
424
431
|
return ok(await runVisibility(ctx, input || {}));
|
|
@@ -430,7 +437,8 @@ export function registerTools(server, ctx) {
|
|
|
430
437
|
|
|
431
438
|
if (ctx.edition !== "local") return; // web edition stops here — no CLI tools
|
|
432
439
|
|
|
433
|
-
// ── CLI-wrapping tools (local edition only)
|
|
440
|
+
// ── CLI-wrapping tools (local edition only) ───────────────────────────────
|
|
441
|
+
|
|
434
442
|
server.tool(
|
|
435
443
|
"cloudgrid_init",
|
|
436
444
|
"Register a new CloudGrid app or agent, optionally seeding a web service. Wraps `cloudgrid init`.",
|
|
@@ -442,6 +450,7 @@ export function registerTools(server, ctx) {
|
|
|
442
450
|
dir: z.string().optional().describe("Target directory. Defaults to ./<name>."),
|
|
443
451
|
org: z.string().optional().describe("Override the active org for this init."),
|
|
444
452
|
},
|
|
453
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
445
454
|
cliTool(({ kind, name, type, description, dir, org }) => {
|
|
446
455
|
const args = ["init", kind, name];
|
|
447
456
|
if (type) args.push("--type", type);
|
|
@@ -460,6 +469,7 @@ export function registerTools(server, ctx) {
|
|
|
460
469
|
org: z.string().optional().describe("Pick or override the org."),
|
|
461
470
|
no_deploy: z.boolean().optional().describe("Register the entity but do not build or deploy."),
|
|
462
471
|
},
|
|
472
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
463
473
|
cliTool(({ target, org, no_deploy }) => {
|
|
464
474
|
const args = ["plug"];
|
|
465
475
|
if (target) args.push(target);
|
|
@@ -478,6 +488,7 @@ export function registerTools(server, ctx) {
|
|
|
478
488
|
tail: z.number().int().positive().optional().describe("Number of recent lines. Default 100."),
|
|
479
489
|
since: z.string().optional().describe("Only logs newer than this, e.g. 5m, 1h, 2d."),
|
|
480
490
|
},
|
|
491
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
481
492
|
cliTool(({ name, tail, since }) => {
|
|
482
493
|
const args = ["logs"];
|
|
483
494
|
if (name) args.push(name);
|
|
@@ -494,6 +505,7 @@ export function registerTools(server, ctx) {
|
|
|
494
505
|
name: z.string().describe("Entity slug."),
|
|
495
506
|
mode: z.enum(["link", "private", "authenticated", "org", "space"]).optional().describe("Visibility mode. Default link."),
|
|
496
507
|
},
|
|
508
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
497
509
|
cliTool(({ name, mode }) => ["visibility", "set", name, mode ?? "link"]),
|
|
498
510
|
);
|
|
499
511
|
|
|
@@ -505,6 +517,7 @@ export function registerTools(server, ctx) {
|
|
|
505
517
|
limit: z.number().int().positive().max(200).optional().describe("Number of events. Default 50, max 200."),
|
|
506
518
|
org: z.string().optional().describe("Read another org's feed where you have access."),
|
|
507
519
|
},
|
|
520
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
508
521
|
cliTool(({ since, limit, org }) => {
|
|
509
522
|
const args = ["feedback", "list"];
|
|
510
523
|
if (since) args.push("--since", since);
|
|
@@ -522,6 +535,7 @@ export function registerTools(server, ctx) {
|
|
|
522
535
|
wait: z.boolean().optional().describe("Wait for the refresh to finish. Default true."),
|
|
523
536
|
org: z.string().optional().describe("Target an entity in another org."),
|
|
524
537
|
},
|
|
538
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
525
539
|
cliTool(({ name, wait, org }) => {
|
|
526
540
|
const args = ["brain", "refresh", name];
|
|
527
541
|
if (wait !== false) args.push("--wait");
|
|
@@ -529,6 +543,214 @@ export function registerTools(server, ctx) {
|
|
|
529
543
|
return args;
|
|
530
544
|
}),
|
|
531
545
|
);
|
|
546
|
+
|
|
547
|
+
// ── New CLI-wrapping tools (local edition only) ───────────────────────────
|
|
548
|
+
|
|
549
|
+
server.tool(
|
|
550
|
+
"cloudgrid_whoami",
|
|
551
|
+
"Show the signed-in user and active org. Wraps `cloudgrid whoami`.",
|
|
552
|
+
{},
|
|
553
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
554
|
+
cliTool(() => ["whoami"]),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
server.tool(
|
|
558
|
+
"cloudgrid_use",
|
|
559
|
+
"Switch the active org. Wraps `cloudgrid use`.",
|
|
560
|
+
{ org: z.string().describe("Org slug to switch to.") },
|
|
561
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
562
|
+
cliTool(({ org }) => ["use", org]),
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
server.tool(
|
|
566
|
+
"cloudgrid_logout",
|
|
567
|
+
"Sign out and clear local credentials. Wraps `cloudgrid logout`.",
|
|
568
|
+
{},
|
|
569
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
570
|
+
cliTool(() => ["logout"]),
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
server.tool(
|
|
574
|
+
"cloudgrid_status",
|
|
575
|
+
"Org dashboard, entity detail, or deploy snapshot. Wraps `cloudgrid status`.",
|
|
576
|
+
{ name: z.string().optional().describe("Entity name or trace id. Omit for the org dashboard.") },
|
|
577
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
578
|
+
cliTool(({ name }) => (name ? ["status", name] : ["status"])),
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
server.tool(
|
|
582
|
+
"cloudgrid_info",
|
|
583
|
+
"Show metadata for a CloudGrid entity. Wraps `cloudgrid info`.",
|
|
584
|
+
{ name: z.string().optional().describe("Entity name. Omit for the entity linked to the current directory.") },
|
|
585
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
586
|
+
cliTool(({ name }) => {
|
|
587
|
+
const args = ["info"];
|
|
588
|
+
if (name) args.push(name);
|
|
589
|
+
return args;
|
|
590
|
+
}),
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
server.tool(
|
|
594
|
+
"cloudgrid_builds",
|
|
595
|
+
"List recent builds and deploys for an entity. Wraps `cloudgrid builds`.",
|
|
596
|
+
{
|
|
597
|
+
name: z.string().optional().describe("Entity name. Omit for the entity linked to the current directory."),
|
|
598
|
+
limit: z.number().int().positive().optional().describe("Number of builds to show."),
|
|
599
|
+
},
|
|
600
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
601
|
+
cliTool(({ name, limit }) => {
|
|
602
|
+
const args = ["builds"];
|
|
603
|
+
if (name) args.push(name);
|
|
604
|
+
if (limit) args.push("--limit", String(limit));
|
|
605
|
+
return args;
|
|
606
|
+
}),
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
server.tool(
|
|
610
|
+
"cloudgrid_grid",
|
|
611
|
+
"List entities on the hub or org. Wraps `cloudgrid grid`.",
|
|
612
|
+
{ org: z.string().optional().describe("Org slug. Omit for the active org.") },
|
|
613
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
614
|
+
cliTool(({ org }) => (org ? ["grid", "--org", org] : ["grid"])),
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
server.tool(
|
|
618
|
+
"cloudgrid_rename",
|
|
619
|
+
"Rename a CloudGrid entity. Wraps `cloudgrid rename`.",
|
|
620
|
+
{
|
|
621
|
+
name: z.string().describe("Current entity slug."),
|
|
622
|
+
new_name: z.string().describe("New slug."),
|
|
623
|
+
},
|
|
624
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
625
|
+
cliTool(({ name, new_name }) => ["rename", name, new_name]),
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
server.tool(
|
|
629
|
+
"cloudgrid_unplug",
|
|
630
|
+
"Take an entity off the grid. Destructive. Wraps `cloudgrid unplug`.",
|
|
631
|
+
{
|
|
632
|
+
name: z.string().describe("Entity slug to take down (required)."),
|
|
633
|
+
confirm: z.literal(true).describe("Must be true to proceed."),
|
|
634
|
+
},
|
|
635
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
636
|
+
cliTool(({ name }) => ["unplug", name]),
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
server.tool(
|
|
640
|
+
"cloudgrid_delete",
|
|
641
|
+
"Archive and delete a CloudGrid entity. Destructive. Wraps `cloudgrid delete`.",
|
|
642
|
+
{
|
|
643
|
+
name: z.string().describe("Entity slug to delete (required)."),
|
|
644
|
+
confirm: z.literal(true).describe("Must be true to proceed."),
|
|
645
|
+
},
|
|
646
|
+
{ readOnlyHint: false, destructiveHint: true, openWorldHint: true },
|
|
647
|
+
cliTool(({ name }) => ["delete", name]),
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
server.tool(
|
|
651
|
+
"cloudgrid_rollback",
|
|
652
|
+
"Rollback an entity to a previous version. Wraps `cloudgrid rollback`.",
|
|
653
|
+
{
|
|
654
|
+
name: z.string().describe("Entity slug."),
|
|
655
|
+
to: z.string().optional().describe("Target version tag or id. Omit to roll back one version."),
|
|
656
|
+
},
|
|
657
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
658
|
+
cliTool(({ name, to }) => {
|
|
659
|
+
const args = ["rollback", name];
|
|
660
|
+
if (to) args.push("--to", to);
|
|
661
|
+
return args;
|
|
662
|
+
}),
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
server.tool(
|
|
666
|
+
"cloudgrid_versions",
|
|
667
|
+
"List published versions for an entity. Wraps `cloudgrid versions`.",
|
|
668
|
+
{ name: z.string().optional().describe("Entity name. Omit for the entity linked to the current directory.") },
|
|
669
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
670
|
+
cliTool(({ name }) => {
|
|
671
|
+
const args = ["versions"];
|
|
672
|
+
if (name) args.push(name);
|
|
673
|
+
return args;
|
|
674
|
+
}),
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
server.tool(
|
|
678
|
+
"cloudgrid_env",
|
|
679
|
+
"Manage environment variables for an entity. Wraps `cloudgrid env`.",
|
|
680
|
+
{
|
|
681
|
+
action: z.enum(["get", "set", "list"]).describe("get, set, or list."),
|
|
682
|
+
name: z.string().describe("Entity slug."),
|
|
683
|
+
key: z.string().optional().describe("Variable name. Required for get and set."),
|
|
684
|
+
value: z.string().optional().describe("Variable value. Required for set."),
|
|
685
|
+
},
|
|
686
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
687
|
+
cliTool(({ action, name, key, value }) => {
|
|
688
|
+
if (action === "set") {
|
|
689
|
+
if (!key || value === undefined) throw new Error("key and value are required for set");
|
|
690
|
+
return ["env", "set", name, key, value];
|
|
691
|
+
}
|
|
692
|
+
if (action === "get") {
|
|
693
|
+
if (!key) throw new Error("key is required for get");
|
|
694
|
+
return ["env", "get", name, key];
|
|
695
|
+
}
|
|
696
|
+
return ["env", "list", name];
|
|
697
|
+
}),
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
server.tool(
|
|
701
|
+
"cloudgrid_secrets",
|
|
702
|
+
"Set or list secret names for an entity. Never returns secret values. Wraps `cloudgrid secrets`.",
|
|
703
|
+
{
|
|
704
|
+
action: z.enum(["set", "list"]).describe("set or list (names only)."),
|
|
705
|
+
name: z.string().describe("Entity slug."),
|
|
706
|
+
key: z.string().optional().describe("Secret name. Required for set."),
|
|
707
|
+
value: z.string().optional().describe("Secret value. Required for set."),
|
|
708
|
+
},
|
|
709
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
710
|
+
cliTool(({ action, name, key, value }) => {
|
|
711
|
+
if (action === "set") {
|
|
712
|
+
if (!key || value === undefined) throw new Error("key and value are required for set");
|
|
713
|
+
return ["secrets", "set", name, key, value];
|
|
714
|
+
}
|
|
715
|
+
return ["secrets", "list", name];
|
|
716
|
+
}),
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
server.tool(
|
|
720
|
+
"cloudgrid_scaffold",
|
|
721
|
+
"Generate starter files for a CloudGrid entity. Wraps `cloudgrid scaffold`.",
|
|
722
|
+
{
|
|
723
|
+
template: z.string().optional().describe("Template name."),
|
|
724
|
+
dir: z.string().optional().describe("Target directory."),
|
|
725
|
+
},
|
|
726
|
+
{ readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
727
|
+
cliTool(({ template, dir }) => {
|
|
728
|
+
const args = ["scaffold"];
|
|
729
|
+
if (template) args.push(template);
|
|
730
|
+
if (dir) args.push("--dir", dir);
|
|
731
|
+
return args;
|
|
732
|
+
}),
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
server.tool(
|
|
736
|
+
"cloudgrid_doctor",
|
|
737
|
+
"Run CloudGrid diagnostics on the local environment. Wraps `cloudgrid doctor`.",
|
|
738
|
+
{},
|
|
739
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
740
|
+
cliTool(() => ["doctor"]),
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
server.tool(
|
|
744
|
+
"cloudgrid_open",
|
|
745
|
+
"Return the public URL for an entity. Does not open a browser. Wraps `cloudgrid open --url`.",
|
|
746
|
+
{ name: z.string().optional().describe("Entity name. Omit for the entity linked to the current directory.") },
|
|
747
|
+
{ readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
748
|
+
cliTool(({ name }) => {
|
|
749
|
+
const args = ["open", "--url"];
|
|
750
|
+
if (name) args.push(name);
|
|
751
|
+
return args;
|
|
752
|
+
}),
|
|
753
|
+
);
|
|
532
754
|
}
|
|
533
755
|
|
|
534
756
|
export { decodeJwt };
|
package/src/web.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// Run: PORT=8080 node src/web.js Health: GET /healthz
|
|
16
16
|
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
17
18
|
import { randomUUID } from "node:crypto";
|
|
18
19
|
import express from "express";
|
|
19
20
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -22,6 +23,8 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
|
22
23
|
import { registerTools, decodeJwt } from "./tools.js";
|
|
23
24
|
import { mountOAuth, bearerChallenge } from "./oauth.js";
|
|
24
25
|
|
|
26
|
+
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url)));
|
|
27
|
+
|
|
25
28
|
const PORT = Number(process.env.PORT || 8080);
|
|
26
29
|
|
|
27
30
|
// This server's public origin — used in OAuth metadata and the interstitial.
|
|
@@ -128,7 +131,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
128
131
|
delete sessionAuth[transport.sessionId];
|
|
129
132
|
}
|
|
130
133
|
};
|
|
131
|
-
const server = new McpServer({ name: "cloudgrid-mcp-web", version
|
|
134
|
+
const server = new McpServer({ name: "cloudgrid-mcp-web", version });
|
|
132
135
|
registerTools(server, makeWebContext(newSessionId));
|
|
133
136
|
await server.connect(transport);
|
|
134
137
|
await transport.handleRequest(req, res, req.body);
|