@groundfloorcloud/mcp 0.1.2 → 0.1.4

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/README.md +6 -2
  2. package/dist/index.js +152 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -67,18 +67,22 @@ The `command` must be your Node (from `which node`), not `npx` and not Cursor’
67
67
  | `coderunner_list` / `_get_status` / `_get_logs` | Inspect workloads |
68
68
  | `apps_list` / `apps_get` | Inspect product Apps |
69
69
  | `secrets_list` / `files_list` / `domains_list` / `dataplane_status` | Inspect (no secrets values) |
70
- | `docs_list` / `docs_get` | Product docs (`coderunner` returns the full type/auth/Dockerfile guide) |
70
+ | `docs_list` / `docs_get` | Product docs (`coderunner`, `coderunner-runtime`, and `shell` are embedded how-tos) |
71
71
  | `suggest_cli` | Command to run: login, deploy, init_app, publish_app, … |
72
72
 
73
73
  Never create `app_kind=coderunner`. Deploy with `gf deploy`.
74
74
 
75
+ **Shell apps:** `docs_get` id `shell`. Always `gf apps init --slug <portal-slug>`,
76
+ then `npm run release` and `gf apps publish --path release.zip`. Do not invent
77
+ `App.tsx` / `vite.config`, zip `dist/`, or upload only `remoteEntry.js`.
78
+
75
79
  **Coderunner how-to** is embedded: `docs_get` id `coderunner` (also
76
80
  `coderunner-overview`). That page covers:
77
81
 
78
82
  - Types: `function`, `job`, `schedule`, `service` (**also called Deployment**)
79
83
  - **Authentication must be active** before creating a coderunner
80
84
  - **Dockerfile is required** for `service` / Deployment
81
- - Node `scripts.start` + listen on 8080
85
+ - Runtime contract: Node `scripts.start`, listen on `PORT`/8080, bind `0.0.0.0` (`docs_get` id `coderunner-runtime`)
82
86
  - Local development against live vault/files/secrets (`gf env` + `gf token`)
83
87
 
84
88
  `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\` (health GET \`/\` or \`/health\`).
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 \u2014
471
- read Process Log (\`runner_id\` = deployment id).
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,10 +585,44 @@ 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
 
592
+ // src/shell-guide.ts
593
+ var SHELL_APP_GUIDE = `
594
+ # Shell federated apps
595
+
596
+ A Groundfloor **App** (app_kind=shell_federated) is a Module Federation remote
597
+ loaded by the workspace Shell. It is **not** a Coderunner. Do not \`gf deploy\`.
598
+
599
+ ## Required path (do not invent a Vite app)
600
+
601
+ Agents must not scaffold App.tsx, vite.config.ts, or BrowserRouter from memory.
602
+ A from-scratch remote 404s in Shell (missing sibling chunks) or crashes
603
+ (extra Router). Federation \`name\` must be federationRemoteName(slug)
604
+ (\`hello-world\` \u2192 \`hello_world\`).
605
+
606
+ \`\`\`bash
607
+ gf apps create --name "Hello World" --slug hello-world --kind shell_federated
608
+ gf apps init --slug hello-world
609
+ cd hello-world
610
+ npm install
611
+ # edit src/ inside the kit only
612
+ npm run release
613
+ gf apps publish --path release.zip
614
+ \`\`\`
615
+
616
+ \`npm run release\` packs **all** of dist/assets (remoteEntry.js + hashed
617
+ \`__federation_*.js\` + CSS) at the zip root. Do not zip \`dist/\`. Do not
618
+ upload a lone \`remoteEntry.js\`. \`gf apps publish\` rejects incomplete zips.
619
+
620
+ ## suggest_cli
621
+
622
+ - init_app \u2192 \`gf apps init --slug <portal-slug>\`
623
+ - publish_app \u2192 init + npm install + npm run release + gf apps publish --path release.zip
624
+ `;
625
+
520
626
  // src/docs.ts
521
627
  var DOCS_ORIGIN = "https://docs.groundfloor.cloud";
522
628
  var DOC_CATALOG = [
@@ -524,7 +630,7 @@ var DOC_CATALOG = [
524
630
  id: "agents",
525
631
  title: "Deploy with agents",
526
632
  path: "/docs/customer-portal/agents",
527
- summary: "How agents should deploy Coderunner workloads and init/publish Shell apps with gf / MCP. Enable workspace Authentication before create. Service (Deployment) requires a Dockerfile."
633
+ summary: "How agents should deploy Coderunner workloads and init/publish Shell apps with gf / MCP. Enable workspace Authentication before create. Service (Deployment) requires a Dockerfile. Shell apps: gf apps init, never invent App.tsx."
528
634
  },
529
635
  {
530
636
  id: "developers",
@@ -536,9 +642,16 @@ var DOC_CATALOG = [
536
642
  id: "coderunner",
537
643
  title: "Coderunner",
538
644
  path: "/docs/customer-portal/coderunner",
539
- 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.",
540
646
  embedded: CODERUNNER_GUIDE
541
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
+ },
542
655
  {
543
656
  id: "coderunner-overview",
544
657
  title: "Coderunner overview",
@@ -550,7 +663,7 @@ var DOC_CATALOG = [
550
663
  id: "authentication",
551
664
  title: "Workspace Authentication",
552
665
  path: "/docs/customer-portal/authentication",
553
- summary: "Workspace site auth (Groundfloor or external) must be active before creating a Coderunner. Separate from portal Members & Roles."
666
+ summary: "Workspace site auth (Groundfloor or external) must be active before creating a Coderunner. Separate from portal Team."
554
667
  },
555
668
  {
556
669
  id: "api-authentication",
@@ -562,7 +675,8 @@ var DOC_CATALOG = [
562
675
  id: "shell",
563
676
  title: "Shell federated apps",
564
677
  path: "/docs/integrations/shell",
565
- summary: "Product Apps vs Coderunner deploy."
678
+ summary: "Product Apps vs Coderunner. Always gf apps init --slug <portal-slug>, then npm run release and gf apps publish --path release.zip. Never zip dist/ or upload only remoteEntry.js.",
679
+ embedded: SHELL_APP_GUIDE
566
680
  }
567
681
  ];
568
682
  function docUrl(entry) {
@@ -633,7 +747,7 @@ function suggestCli(intent, args) {
633
747
  case "deploy":
634
748
  return {
635
749
  command: name ? `gf deploy -n ${name}${ws}` : `gf deploy${ws}`,
636
- 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 needs package.json scripts.start. Do not create an App for this.",
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.",
637
751
  docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
638
752
  };
639
753
  case "run":
@@ -648,18 +762,24 @@ function suggestCli(intent, args) {
648
762
  why: "Inspect build/deploy state.",
649
763
  docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
650
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
+ };
651
771
  case "init_app": {
652
772
  const slug = args?.slug?.trim();
653
773
  return {
654
774
  command: slug ? `gf apps init --slug ${slug}` : "gf apps init --slug <portal-slug>",
655
- why: "Download the official Shell starter-kit and stamp APP_ID. Never scaffold a federated App.tsx from memory \u2014 BrowserRouter in App.tsx crashes the Shell.",
775
+ why: "Download the official Shell starter-kit and stamp APP_ID. Never scaffold vite.config / App.tsx from memory. Never wrap App in BrowserRouter. Never zip dist/ or upload only remoteEntry.js.",
656
776
  docs: "https://docs.groundfloor.cloud/docs/integrations/shell"
657
777
  };
658
778
  }
659
779
  case "publish_app":
660
780
  return {
661
781
  command: args?.slug ? `gf apps init --slug ${args.slug} && cd ${args.slug} && npm install && npm run release && gf apps publish --path release.zip` : "gf apps init --slug <portal-slug> && cd <slug> && npm install && npm run release && gf apps publish --path release.zip",
662
- why: "Ship a Shell federated product App. Init the starter-kit first; run npm run release (do not zip Vite dist/). Do not gf deploy and do not invent App.tsx.",
782
+ why: "Ship a Shell federated App. Always init the kit first. npm run release (not zip dist/, not a lone remoteEntry.js). Do not gf deploy. Do not invent App.tsx.",
663
783
  docs: "https://docs.groundfloor.cloud/docs/integrations/shell"
664
784
  };
665
785
  case "secrets":
@@ -721,7 +841,7 @@ async function resolveCoderunnerId(apiUrl, workspaceId, idOrSlug) {
721
841
  }
722
842
  var server = new McpServer({
723
843
  name: "groundfloor",
724
- version: "0.1.0"
844
+ version: "0.1.4"
725
845
  });
726
846
  server.tool(
727
847
  "whoami",
@@ -748,7 +868,7 @@ server.tool(
748
868
  );
749
869
  server.tool(
750
870
  "workspace_context",
751
- "Live context for the selected workspace: session, coderunners, apps, dataplane, suggested gf commands, and Coderunner type/auth/Dockerfile rules. Use docs_get id coderunner for the full how-to.",
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.",
752
872
  { workspace_id: wsId },
753
873
  async ({ workspace_id }) => {
754
874
  try {
@@ -781,20 +901,29 @@ server.tool(
781
901
  dataplane,
782
902
  coderunner_howto: {
783
903
  docs_get: "coderunner",
904
+ runtime_contract: "coderunner-runtime",
784
905
  before_create: "Enable Administer \u2192 Authentication (mode groundfloor or external, realm active). Mode none cannot mint a Code Runner JWT.",
785
906
  types: {
786
- 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.",
787
908
  job: "Run-to-completion worker. Deploy, then gf coderunner run.",
788
909
  schedule: "Cron job. No public Deployment URL. Set schedule/cron at create or deploy.",
789
- 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."
790
911
  },
791
- node: 'Bootstrap image uses CMD ["npm","start"]. Missing scripts.start \u2192 deploy Failed (status 4), 8080 connection refused.',
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.",
792
913
  local_live_data: 'eval "$(gf env)" then call Control Plane vault/files/secrets with Bearer $(gf token). Do not zip .env files.'
793
914
  },
915
+ shell_app_howto: {
916
+ docs_get: "shell",
917
+ rule: "Never invent a Vite + Module Federation app. gf apps init --slug <portal-slug>, npm install, npm run release, gf apps publish --path release.zip. Do not zip dist/ or upload only remoteEntry.js.",
918
+ suggest_cli: "init_app | publish_app"
919
+ },
794
920
  actions: {
795
921
  deploy: suggestCli("deploy"),
796
922
  switch_workspace: suggestCli("switch_workspace"),
797
- run: suggestCli("run")
923
+ run: suggestCli("run"),
924
+ logs: suggestCli("logs"),
925
+ init_app: suggestCli("init_app"),
926
+ publish_app: suggestCli("publish_app")
798
927
  },
799
928
  docs: DOC_CATALOG.map((d) => ({
800
929
  id: d.id,
@@ -870,7 +999,7 @@ server.tool(
870
999
  );
871
1000
  server.tool(
872
1001
  "coderunner_get_logs",
873
- "Fetch logs for a job id from a previous `gf coderunner run`.",
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`).",
874
1003
  {
875
1004
  workspace_id: wsId,
876
1005
  coderunner_id: z.string().min(1),
@@ -998,10 +1127,10 @@ server.tool(
998
1127
  );
999
1128
  server.tool(
1000
1129
  "docs_get",
1001
- "How-to for Coderunner types, Dockerfile, Authentication, local live data, gf CLI, Shell. Pass id from docs_list (coderunner, coderunner-overview, agents, developers, authentication, api-authentication, shell). Prefer id coderunner before deploying.",
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.",
1002
1131
  {
1003
1132
  id: z.string().min(1).describe(
1004
- "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"
1005
1134
  )
1006
1135
  },
1007
1136
  async ({ id }) => {
@@ -1022,7 +1151,7 @@ server.tool(
1022
1151
  );
1023
1152
  server.tool(
1024
1153
  "suggest_cli",
1025
- "Return the gf command the developer (or agent terminal) should run. MCP does not execute mutations.",
1154
+ "Return the gf command to run. For a Shell App use intent init_app or publish_app (never scaffold App.tsx; never gf deploy). MCP does not execute mutations.",
1026
1155
  {
1027
1156
  intent: z.enum([
1028
1157
  "login",
@@ -1030,13 +1159,14 @@ server.tool(
1030
1159
  "deploy",
1031
1160
  "run",
1032
1161
  "status",
1162
+ "logs",
1033
1163
  "init_app",
1034
1164
  "publish_app",
1035
1165
  "secrets",
1036
1166
  "dataplane"
1037
1167
  ]),
1038
1168
  workspace: z.string().optional(),
1039
- 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"),
1040
1170
  slug: z.string().optional().describe("Portal app slug for init_app / publish_app"),
1041
1171
  cell: z.enum(["production", "stage", "dev"]).optional()
1042
1172
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundfloorcloud/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Groundfloor MCP — inspect workspace state and docs; take actions with the gf CLI.",
5
5
  "type": "module",
6
6
  "bin": {