@groundfloorcloud/mcp 0.1.3 → 0.1.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.
- package/README.md +9 -4
- package/dist/index.js +104 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,9 @@ They are not replacements. Sign in with `gf`; the MCP reads that session and
|
|
|
7
7
|
docs so the agent knows *what is in the workspace* and *which `gf` command to
|
|
8
8
|
run*.
|
|
9
9
|
|
|
10
|
+
Use this package (`@groundfloorcloud/mcp`). Do **not** install
|
|
11
|
+
`@groundfloor/coderunner-mcp` — it is not published and is not the product MCP.
|
|
12
|
+
|
|
10
13
|
## How the MCP gets context
|
|
11
14
|
|
|
12
15
|
| Source | What it answers |
|
|
@@ -18,11 +21,11 @@ run*.
|
|
|
18
21
|
|
|
19
22
|
Switch workspace: `gf workspaces use <uuid>` (then MCP sees it). Expired session: `gf login` again.
|
|
20
23
|
|
|
21
|
-
##
|
|
24
|
+
## Config
|
|
22
25
|
|
|
23
26
|
`gf` must be on `PATH` and you must have run `gf login` (and usually `gf workspaces use`).
|
|
24
27
|
|
|
25
|
-
Clients that spawn a real Node (Claude Code,
|
|
28
|
+
Clients that spawn a real Node (Claude Code, Codex, many IDEs) can use:
|
|
26
29
|
|
|
27
30
|
```json
|
|
28
31
|
{
|
|
@@ -35,6 +38,8 @@ Clients that spawn a real Node (Claude Code, some IDEs) can use:
|
|
|
35
38
|
}
|
|
36
39
|
```
|
|
37
40
|
|
|
41
|
+
The Shell starter-kit ships this as `.mcp.json`.
|
|
42
|
+
|
|
38
43
|
**Cursor on macOS:** do not use bare `npx`. Cursor runs npm with its bundled Node, which then looks for `Cursor.app/.../resources/lib` and fails with `ENOENT`. Install the package with *your* Node, then point MCP at that binary:
|
|
39
44
|
|
|
40
45
|
```bash
|
|
@@ -67,7 +72,7 @@ The `command` must be your Node (from `which node`), not `npx` and not Cursor’
|
|
|
67
72
|
| `coderunner_list` / `_get_status` / `_get_logs` | Inspect workloads |
|
|
68
73
|
| `apps_list` / `apps_get` | Inspect product Apps |
|
|
69
74
|
| `secrets_list` / `files_list` / `domains_list` / `dataplane_status` | Inspect (no secrets values) |
|
|
70
|
-
| `docs_list` / `docs_get` | Product docs (`coderunner` and `shell` are embedded how-tos) |
|
|
75
|
+
| `docs_list` / `docs_get` | Product docs (`coderunner`, `coderunner-runtime`, and `shell` are embedded how-tos) |
|
|
71
76
|
| `suggest_cli` | Command to run: login, deploy, init_app, publish_app, … |
|
|
72
77
|
|
|
73
78
|
Never create `app_kind=coderunner`. Deploy with `gf deploy`.
|
|
@@ -82,7 +87,7 @@ then `npm run release` and `gf apps publish --path release.zip`. Do not invent
|
|
|
82
87
|
- Types: `function`, `job`, `schedule`, `service` (**also called Deployment**)
|
|
83
88
|
- **Authentication must be active** before creating a coderunner
|
|
84
89
|
- **Dockerfile is required** for `service` / Deployment
|
|
85
|
-
- Node `scripts.start
|
|
90
|
+
- Runtime contract: Node `scripts.start`, listen on `PORT`/8080, bind `0.0.0.0` (`docs_get` id `coderunner-runtime`)
|
|
86
91
|
- Local development against live vault/files/secrets (`gf env` + `gf token`)
|
|
87
92
|
|
|
88
93
|
`workspace_context` includes a `coderunner_howto` summary so agents see this
|
package/dist/index.js
CHANGED
|
@@ -380,6 +380,71 @@ async function resolveMcpConfig(overrides) {
|
|
|
380
380
|
return { apiUrl, workspaceId, token };
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
// src/coderunner-runtime.ts
|
|
384
|
+
var CODERUNNER_RUNTIME_BODY = `
|
|
385
|
+
When a ZIP has **no Dockerfile**, Coderunner injects a language bootstrap image.
|
|
386
|
+
Your process must match that image \u2014 \`gf deploy\` checks the Node bits before
|
|
387
|
+
upload. A custom Dockerfile (required for \`service\` / Deployment) replaces
|
|
388
|
+
the bootstrap; the listen/bind rules still apply.
|
|
389
|
+
|
|
390
|
+
## Node (no Dockerfile)
|
|
391
|
+
|
|
392
|
+
Bootstrap is effectively:
|
|
393
|
+
|
|
394
|
+
\`\`\`dockerfile
|
|
395
|
+
FROM node:18-alpine
|
|
396
|
+
WORKDIR /app
|
|
397
|
+
COPY . .
|
|
398
|
+
EXPOSE 8080
|
|
399
|
+
ENV PORT=8080
|
|
400
|
+
CMD ["npm", "start"]
|
|
401
|
+
\`\`\`
|
|
402
|
+
|
|
403
|
+
1. \`package.json\` **must** define \`"scripts": { "start": "node <entrypoint>" }\`.
|
|
404
|
+
Missing \`start\` \u2192 container crash loop, probe \`127.0.0.1:8080\` connection
|
|
405
|
+
refused, deployment **Failed (status 4)** after several minutes.
|
|
406
|
+
2. Listen on \`Number(process.env.PORT || 8080)\`.
|
|
407
|
+
3. Bind **\`0.0.0.0\`**, not \`127.0.0.1\`. Cluster traffic is not loopback.
|
|
408
|
+
|
|
409
|
+
\`\`\`js
|
|
410
|
+
const port = Number(process.env.PORT || 8080);
|
|
411
|
+
server.listen(port, "0.0.0.0", () => {
|
|
412
|
+
console.log(\`listening on 0.0.0.0:\${port}\`);
|
|
413
|
+
});
|
|
414
|
+
\`\`\`
|
|
415
|
+
|
|
416
|
+
Offer GET \`/\` or \`/health\` for the probe. Sample: \`samples/hello-fn/\`.
|
|
417
|
+
|
|
418
|
+
## Node (with Dockerfile)
|
|
419
|
+
|
|
420
|
+
You own \`CMD\`. If it is \`npm start\`, you still need \`scripts.start\`.
|
|
421
|
+
Listen on 8080 / \`PORT\` and bind \`0.0.0.0\`. \`service\` (Deployment)
|
|
422
|
+
**requires** a root Dockerfile \u2014 bootstrap is not injected on that path.
|
|
423
|
+
|
|
424
|
+
## Python
|
|
425
|
+
|
|
426
|
+
Bind \`0.0.0.0\` and \`PORT\` (default 8080), for example
|
|
427
|
+
\`uvicorn main:app --host 0.0.0.0 --port \${PORT:-8080}\`.
|
|
428
|
+
|
|
429
|
+
## Jobs and schedules
|
|
430
|
+
|
|
431
|
+
Same package/start rules. The process may exit 0 when finished; it does not
|
|
432
|
+
need a public URL. Schedules have **no** Deployment URL.
|
|
433
|
+
|
|
434
|
+
## What \`gf deploy\` checks
|
|
435
|
+
|
|
436
|
+
- \`service\` without a root Dockerfile \u2192 fail immediately.
|
|
437
|
+
- Node without a Dockerfile (or a Dockerfile that runs \`npm start\`) and no
|
|
438
|
+
\`scripts.start\` \u2192 fail immediately with this contract.
|
|
439
|
+
- Listen/bind are not parsed from source; match the sample or the probe fails
|
|
440
|
+
at deploy time. Check Process Log / \`gf coderunner logs -c <id>\`.
|
|
441
|
+
`.trim();
|
|
442
|
+
var CODERUNNER_RUNTIME_CONTRACT = `
|
|
443
|
+
# Runtime contract
|
|
444
|
+
|
|
445
|
+
${CODERUNNER_RUNTIME_BODY}
|
|
446
|
+
`.trim();
|
|
447
|
+
|
|
383
448
|
// src/coderunner-guide.ts
|
|
384
449
|
var CODERUNNER_GUIDE = `
|
|
385
450
|
# Coderunner workloads
|
|
@@ -391,6 +456,12 @@ The Customer Portal, \`gf\` CLI, and this MCP all drive the same lifecycle.
|
|
|
391
456
|
Do **not** create an App to run code. \`POST \u2026/apps\` with \`app_kind=coderunner\`
|
|
392
457
|
is rejected. Optional Apps are Shell / product packaging only.
|
|
393
458
|
|
|
459
|
+
## Runtime contract
|
|
460
|
+
|
|
461
|
+
${CODERUNNER_RUNTIME_BODY}
|
|
462
|
+
|
|
463
|
+
Also \`docs_get\` id \`coderunner-runtime\`.
|
|
464
|
+
|
|
394
465
|
## Prerequisites (do these before create/deploy)
|
|
395
466
|
|
|
396
467
|
1. **Workspace Authentication is required** before creating a coderunner.
|
|
@@ -421,7 +492,7 @@ Pick **one** type per coderunner. Changing type later means a new coderunner.
|
|
|
421
492
|
### Function
|
|
422
493
|
|
|
423
494
|
- ZIP at archive root (\`package.json\` / \`index.js\` or \`main.py\` not nested).
|
|
424
|
-
- Listen on \`process.env.PORT || 8080
|
|
495
|
+
- Listen on \`process.env.PORT || 8080\`, bind **\`0.0.0.0\`** (health GET \`/\` or \`/health\`).
|
|
425
496
|
- **Node:** \`package.json\` **must** include \`"scripts": { "start": "node index.js" }\`.
|
|
426
497
|
The runtime image is \`CMD ["npm", "start"]\`. Missing \`start\` \u2192 crash loop,
|
|
427
498
|
probe \`127.0.0.1:8080\` connection refused, deployment **Failed (status 4)**.
|
|
@@ -467,8 +538,9 @@ CMD ["npm", "start"]
|
|
|
467
538
|
coderunner if the slug is new; otherwise uploads a new version and deploys it.
|
|
468
539
|
- Status: upload/build \`1\u20134\` (3=Completed, 4=Failed). Deployment \`1\u20137\`
|
|
469
540
|
(3=Deployed, 4=Failed). A templated \`deployment_url\` with empty
|
|
470
|
-
\`externalEndpoint\` and status 4 means the container never became ready
|
|
471
|
-
|
|
541
|
+
\`externalEndpoint\` and status 4 means the container never became ready.
|
|
542
|
+
\`gf deploy\` prints the last Process Log lines, ids, and a Portal link on
|
|
543
|
+
failure. Fetch more with \`gf coderunner logs -c <id|slug>\`.
|
|
472
544
|
- Strip \`.env*\`, \`.git\`, \`node_modules\` from ZIPs. Put secrets in
|
|
473
545
|
\`gf secrets\` / deploy \`-e\`, never in the archive.
|
|
474
546
|
- Zip **contents** at root, not a wrapping folder.
|
|
@@ -513,7 +585,7 @@ When the handler looks right locally, \`gf deploy\` the same tree.
|
|
|
513
585
|
|
|
514
586
|
## Agent actions
|
|
515
587
|
|
|
516
|
-
Inspect: \`workspace_context\`, \`docs_get\` id \`coderunner\`, \`suggest_cli\`.
|
|
588
|
+
Inspect: \`workspace_context\`, \`docs_get\` id \`coderunner\` or \`coderunner-runtime\`, \`suggest_cli\`.
|
|
517
589
|
Act: \`gf deploy\`, \`gf coderunner run|status\`. MCP does not deploy.
|
|
518
590
|
`.trim();
|
|
519
591
|
|
|
@@ -570,9 +642,16 @@ var DOC_CATALOG = [
|
|
|
570
642
|
id: "coderunner",
|
|
571
643
|
title: "Coderunner",
|
|
572
644
|
path: "/docs/customer-portal/coderunner",
|
|
573
|
-
summary: "Workload types (function, job, schedule, service/Deployment), Dockerfile for services, Authentication before create, local live data, gf deploy.",
|
|
645
|
+
summary: "Workload types (function, job, schedule, service/Deployment), runtime contract (scripts.start, 0.0.0.0:8080), Dockerfile for services, Authentication before create, local live data, gf deploy.",
|
|
574
646
|
embedded: CODERUNNER_GUIDE
|
|
575
647
|
},
|
|
648
|
+
{
|
|
649
|
+
id: "coderunner-runtime",
|
|
650
|
+
title: "Coderunner runtime contract",
|
|
651
|
+
path: "/docs/customer-portal/coderunner",
|
|
652
|
+
summary: "What the platform expects your code to do: Node bootstrap (npm start, PORT/8080, bind 0.0.0.0), Python bind, service Dockerfile.",
|
|
653
|
+
embedded: CODERUNNER_RUNTIME_CONTRACT
|
|
654
|
+
},
|
|
576
655
|
{
|
|
577
656
|
id: "coderunner-overview",
|
|
578
657
|
title: "Coderunner overview",
|
|
@@ -584,7 +663,7 @@ var DOC_CATALOG = [
|
|
|
584
663
|
id: "authentication",
|
|
585
664
|
title: "Workspace Authentication",
|
|
586
665
|
path: "/docs/customer-portal/authentication",
|
|
587
|
-
summary: "Workspace site auth (Groundfloor or external) must be active before creating a Coderunner. Separate from portal
|
|
666
|
+
summary: "Workspace site auth (Groundfloor or external) must be active before creating a Coderunner. Separate from portal Team."
|
|
588
667
|
},
|
|
589
668
|
{
|
|
590
669
|
id: "api-authentication",
|
|
@@ -668,7 +747,7 @@ function suggestCli(intent, args) {
|
|
|
668
747
|
case "deploy":
|
|
669
748
|
return {
|
|
670
749
|
command: name ? `gf deploy -n ${name}${ws}` : `gf deploy${ws}`,
|
|
671
|
-
why: "Package the current (or --path) folder and deploy as a Coderunner. Workspace Authentication (groundfloor/external) must already be active. workloadType function|job|schedule|service (service = Deployment; requires a Dockerfile). Node
|
|
750
|
+
why: "Package the current (or --path) folder and deploy as a Coderunner. Workspace Authentication (groundfloor/external) must already be active. workloadType function|job|schedule|service (service = Deployment; requires a Dockerfile). Node bootstrap: scripts.start, listen PORT||8080, bind 0.0.0.0. Do not create an App for this.",
|
|
672
751
|
docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
|
|
673
752
|
};
|
|
674
753
|
case "run":
|
|
@@ -683,6 +762,12 @@ function suggestCli(intent, args) {
|
|
|
683
762
|
why: "Inspect build/deploy state.",
|
|
684
763
|
docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
|
|
685
764
|
};
|
|
765
|
+
case "logs":
|
|
766
|
+
return {
|
|
767
|
+
command: name ? `gf coderunner logs -c ${name}${ws}` : `gf coderunner logs -c <id-or-slug>${ws}`,
|
|
768
|
+
why: "Process Log (stdout/stderr). gf deploy prints the last lines on failure; this command fetches more. runner_id accepts a coderunner id.",
|
|
769
|
+
docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
|
|
770
|
+
};
|
|
686
771
|
case "init_app": {
|
|
687
772
|
const slug = args?.slug?.trim();
|
|
688
773
|
return {
|
|
@@ -756,7 +841,7 @@ async function resolveCoderunnerId(apiUrl, workspaceId, idOrSlug) {
|
|
|
756
841
|
}
|
|
757
842
|
var server = new McpServer({
|
|
758
843
|
name: "groundfloor",
|
|
759
|
-
version: "0.1.
|
|
844
|
+
version: "0.1.5"
|
|
760
845
|
});
|
|
761
846
|
server.tool(
|
|
762
847
|
"whoami",
|
|
@@ -783,7 +868,7 @@ server.tool(
|
|
|
783
868
|
);
|
|
784
869
|
server.tool(
|
|
785
870
|
"workspace_context",
|
|
786
|
-
"Live context for the selected workspace: session, coderunners, apps, dataplane, suggested gf commands, and Coderunner type/auth/Dockerfile rules. Use docs_get id coderunner
|
|
871
|
+
"Live context for the selected workspace: session, coderunners, apps, dataplane, suggested gf commands, and Coderunner type/auth/Dockerfile/runtime-contract rules. Use docs_get id coderunner (or coderunner-runtime) before deploying.",
|
|
787
872
|
{ workspace_id: wsId },
|
|
788
873
|
async ({ workspace_id }) => {
|
|
789
874
|
try {
|
|
@@ -816,14 +901,15 @@ server.tool(
|
|
|
816
901
|
dataplane,
|
|
817
902
|
coderunner_howto: {
|
|
818
903
|
docs_get: "coderunner",
|
|
904
|
+
runtime_contract: "coderunner-runtime",
|
|
819
905
|
before_create: "Enable Administer \u2192 Authentication (mode groundfloor or external, realm active). Mode none cannot mint a Code Runner JWT.",
|
|
820
906
|
types: {
|
|
821
|
-
function: "HTTP handler on PORT/8080. Invoke with gf coderunner run or the Deployment URL. Node requires package.json scripts.start.",
|
|
907
|
+
function: "HTTP handler on PORT/8080 bound to 0.0.0.0. Invoke with gf coderunner run or the Deployment URL. Node requires package.json scripts.start.",
|
|
822
908
|
job: "Run-to-completion worker. Deploy, then gf coderunner run.",
|
|
823
909
|
schedule: "Cron job. No public Deployment URL. Set schedule/cron at create or deploy.",
|
|
824
|
-
service: "Long-running Deployment (same type as service). Dockerfile at ZIP root is required. Listen on 8080."
|
|
910
|
+
service: "Long-running Deployment (same type as service). Dockerfile at ZIP root is required. Listen on 8080 / 0.0.0.0."
|
|
825
911
|
},
|
|
826
|
-
node:
|
|
912
|
+
node: "No Dockerfile \u2192 platform injects node:18-alpine CMD npm start EXPOSE 8080. Require scripts.start; listen on PORT||8080; bind 0.0.0.0. gf deploy fails fast if scripts.start is missing.",
|
|
827
913
|
local_live_data: 'eval "$(gf env)" then call Control Plane vault/files/secrets with Bearer $(gf token). Do not zip .env files.'
|
|
828
914
|
},
|
|
829
915
|
shell_app_howto: {
|
|
@@ -835,6 +921,7 @@ server.tool(
|
|
|
835
921
|
deploy: suggestCli("deploy"),
|
|
836
922
|
switch_workspace: suggestCli("switch_workspace"),
|
|
837
923
|
run: suggestCli("run"),
|
|
924
|
+
logs: suggestCli("logs"),
|
|
838
925
|
init_app: suggestCli("init_app"),
|
|
839
926
|
publish_app: suggestCli("publish_app")
|
|
840
927
|
},
|
|
@@ -912,7 +999,7 @@ server.tool(
|
|
|
912
999
|
);
|
|
913
1000
|
server.tool(
|
|
914
1001
|
"coderunner_get_logs",
|
|
915
|
-
"
|
|
1002
|
+
"Job-run logs from a previous `gf coderunner run` (needs job_id). For deploy/build stdout use suggest_cli intent logs (`gf coderunner logs`).",
|
|
916
1003
|
{
|
|
917
1004
|
workspace_id: wsId,
|
|
918
1005
|
coderunner_id: z.string().min(1),
|
|
@@ -1040,10 +1127,10 @@ server.tool(
|
|
|
1040
1127
|
);
|
|
1041
1128
|
server.tool(
|
|
1042
1129
|
"docs_get",
|
|
1043
|
-
"How-to for Coderunner types, Dockerfile, Authentication, local live data, gf CLI, Shell. Pass id from docs_list
|
|
1130
|
+
"How-to for Coderunner types, runtime contract (scripts.start, 0.0.0.0:8080), Dockerfile, Authentication, local live data, gf CLI, Shell. Pass id from docs_list. Prefer id coderunner or coderunner-runtime before deploying.",
|
|
1044
1131
|
{
|
|
1045
1132
|
id: z.string().min(1).describe(
|
|
1046
|
-
"Catalog id, e.g. coderunner | coderunner-overview | agents | developers | authentication | api-authentication | shell"
|
|
1133
|
+
"Catalog id, e.g. coderunner | coderunner-runtime | coderunner-overview | agents | developers | authentication | api-authentication | shell"
|
|
1047
1134
|
)
|
|
1048
1135
|
},
|
|
1049
1136
|
async ({ id }) => {
|
|
@@ -1072,13 +1159,14 @@ server.tool(
|
|
|
1072
1159
|
"deploy",
|
|
1073
1160
|
"run",
|
|
1074
1161
|
"status",
|
|
1162
|
+
"logs",
|
|
1075
1163
|
"init_app",
|
|
1076
1164
|
"publish_app",
|
|
1077
1165
|
"secrets",
|
|
1078
1166
|
"dataplane"
|
|
1079
1167
|
]),
|
|
1080
1168
|
workspace: z.string().optional(),
|
|
1081
|
-
name: z.string().optional().describe("Coderunner name/id for deploy/run/status"),
|
|
1169
|
+
name: z.string().optional().describe("Coderunner name/id for deploy/run/status/logs"),
|
|
1082
1170
|
slug: z.string().optional().describe("Portal app slug for init_app / publish_app"),
|
|
1083
1171
|
cell: z.enum(["production", "stage", "dev"]).optional()
|
|
1084
1172
|
},
|