@groundfloorcloud/mcp 0.1.1 → 0.1.3

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 +17 -1
  2. package/dist/index.js +239 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -67,11 +67,27 @@ 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 |
70
+ | `docs_list` / `docs_get` | Product docs (`coderunner` 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
+
79
+ **Coderunner how-to** is embedded: `docs_get` id `coderunner` (also
80
+ `coderunner-overview`). That page covers:
81
+
82
+ - Types: `function`, `job`, `schedule`, `service` (**also called Deployment**)
83
+ - **Authentication must be active** before creating a coderunner
84
+ - **Dockerfile is required** for `service` / Deployment
85
+ - Node `scripts.start` + listen on 8080
86
+ - Local development against live vault/files/secrets (`gf env` + `gf token`)
87
+
88
+ `workspace_context` includes a `coderunner_howto` summary so agents see this
89
+ without an extra fetch.
90
+
75
91
  ## Build (from this repo)
76
92
 
77
93
  ```bash
package/dist/index.js CHANGED
@@ -380,6 +380,177 @@ async function resolveMcpConfig(overrides) {
380
380
  return { apiUrl, workspaceId, token };
381
381
  }
382
382
 
383
+ // src/coderunner-guide.ts
384
+ var CODERUNNER_GUIDE = `
385
+ # Coderunner workloads
386
+
387
+ Coderunner is Groundfloor's **apps runtime**. Deploy Node.js, Python, .NET, and
388
+ other workloads as **functions**, **jobs**, **schedules**, or **services**.
389
+ The Customer Portal, \`gf\` CLI, and this MCP all drive the same lifecycle.
390
+
391
+ Do **not** create an App to run code. \`POST \u2026/apps\` with \`app_kind=coderunner\`
392
+ is rejected. Optional Apps are Shell / product packaging only.
393
+
394
+ ## Prerequisites (do these before create/deploy)
395
+
396
+ 1. **Workspace Authentication is required** before creating a coderunner.
397
+ Portal \u2192 Administer \u2192 Authentication \u2192 set mode to \`groundfloor\` (or
398
+ \`external\` with an issuer) and wait until the realm is **active**.
399
+ Code Runner JWTs use the workspace site issuer. Mode \`none\` cannot mint
400
+ that token \u2014 create/deploy fails with "requires the workspace site issuer".
401
+ 2. \`gf login\` (\`--stage\` / \`--dev\` as needed) and \`gf workspaces use <uuid>\`.
402
+ 3. Dataplane for vault/files/secrets: \`gf dataplane provision\` if those APIs
403
+ are needed (local live data or the deployed workload).
404
+
405
+ ## Workload types
406
+
407
+ Portal \`workload_type\` (CLI \`--workload-type\` / \`groundfloor.json\`
408
+ \`workloadType\`) maps to upstream \`deploymentType\`. **Service and Deployment
409
+ are the same type** \u2014 portal id \`service\`, UI often says Deployment,
410
+ upstream \`deploymentType=4\`.
411
+
412
+ | Portal id | Also called | Upstream | When to use | How it runs |
413
+ |-----------|-------------|----------|-------------|-------------|
414
+ | \`function\` | Function | 1 | Request/response HTTP handler (webhooks, APIs, agent invoke) | Scale-to-zero Knative. Invoke via Run or Deployment URL. |
415
+ | \`job\` | Background job | 2 | One-shot batch (import, report, backfill) | Run-to-completion. Deploy, then \`gf coderunner run\`. |
416
+ | \`schedule\` | Scheduled job | 3 | Cron (reminders, sync) | Same as job, plus cron. **No** public Deployment URL. |
417
+ | \`service\` | **Deployment** | 4 | Long-running HTTP process (web app, always-on API) | Stays up (replicas). Public Deployment URL. Custom domains when App-bound. |
418
+
419
+ Pick **one** type per coderunner. Changing type later means a new coderunner.
420
+
421
+ ### Function
422
+
423
+ - ZIP at archive root (\`package.json\` / \`index.js\` or \`main.py\` not nested).
424
+ - Listen on \`process.env.PORT || 8080\` (health GET \`/\` or \`/health\`).
425
+ - **Node:** \`package.json\` **must** include \`"scripts": { "start": "node index.js" }\`.
426
+ The runtime image is \`CMD ["npm", "start"]\`. Missing \`start\` \u2192 crash loop,
427
+ probe \`127.0.0.1:8080\` connection refused, deployment **Failed (status 4)**.
428
+ - Python: process must bind 0.0.0.0:\${PORT:-8080}.
429
+ - After deploy: \`gf coderunner run -c <slug> --payload '{"hello":"world"}'\`.
430
+
431
+ ### Job
432
+
433
+ - Same package rules as function. No need to serve forever; exit 0 on success.
434
+ - Deploy, then start a run (\`gf coderunner run\`). Check Process Log.
435
+
436
+ ### Schedule
437
+
438
+ - Same as job, plus a cron expression at create/deploy (\`schedule\` /
439
+ \`CronExpression\`).
440
+ - No public invoke URL. Debug with Process Log, not curl.
441
+
442
+ ### Service (Deployment)
443
+
444
+ - Long-running HTTP. **You must include a \`Dockerfile\` at the ZIP root.**
445
+ Kaniko builds with \`--dockerfile=Dockerfile\`. Language bootstrap does **not**
446
+ replace a missing Dockerfile for this type.
447
+ - Dockerfile must expose and listen on **8080** (or \`PORT\`).
448
+ - Node example:
449
+
450
+ \`\`\`dockerfile
451
+ FROM node:18-alpine
452
+ WORKDIR /app
453
+ COPY package*.json ./
454
+ RUN npm ci --omit=dev
455
+ COPY . .
456
+ EXPOSE 8080
457
+ ENV PORT=8080
458
+ CMD ["npm", "start"]
459
+ \`\`\`
460
+
461
+ - Still require Node \`"start"\` if CMD is \`npm start\`.
462
+ - Prefer curling the Deployment URL (\`/health\`) over \`\u2026/run\`.
463
+
464
+ ## Latest deploy rules (all types)
465
+
466
+ - \`gf deploy\` from the project dir (or \`--path\` / \`--git\`). Creates the
467
+ coderunner if the slug is new; otherwise uploads a new version and deploys it.
468
+ - Status: upload/build \`1\u20134\` (3=Completed, 4=Failed). Deployment \`1\u20137\`
469
+ (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).
472
+ - Strip \`.env*\`, \`.git\`, \`node_modules\` from ZIPs. Put secrets in
473
+ \`gf secrets\` / deploy \`-e\`, never in the archive.
474
+ - Zip **contents** at root, not a wrapping folder.
475
+ - Optional \`groundfloor.json\`: \`name\`, \`runtime\`, \`workloadType\`,
476
+ \`cpu\`, \`memory\`, \`env\`.
477
+
478
+ ## Local development with live workspace data
479
+
480
+ Run the process on your laptop; call the **same** Control Plane + Dataplane
481
+ the deployed coderunner uses. Do not copy production data into fixtures.
482
+
483
+ \`\`\`bash
484
+ gf login --stage # or --dev / production
485
+ gf workspaces use <uuid>
486
+ eval "$(gf env)" # GROUNDFLOOR_TOKEN, CONTROLPLANE_URL, GROUNDFLOOR_WORKSPACE_ID
487
+ gf dataplane status # provision if needed: gf dataplane provision
488
+ gf secrets ls
489
+ \`\`\`
490
+
491
+ Local HTTP (Node function/service):
492
+
493
+ \`\`\`bash
494
+ export PORT=8080
495
+ npm start # same entrypoint as production
496
+ # other terminal:
497
+ curl -sS -X POST http://127.0.0.1:8080/ -H 'Content-Type: application/json' \\
498
+ -d '{"hello":"local"}'
499
+ \`\`\`
500
+
501
+ Live vault / files / secrets from the laptop:
502
+
503
+ \`\`\`bash
504
+ curl -sS -H "Authorization: Bearer $(gf token)" \\
505
+ "$CONTROLPLANE_URL/v1/workspaces/$GROUNDFLOOR_WORKSPACE_ID/vault/collections"
506
+ \`\`\`
507
+
508
+ In app code, use \`CONTROLPLANE_URL\` + Bearer (\`gf token\` locally;
509
+ injected env / workspace secrets after \`gf deploy\`). Never put
510
+ \`DATAPLANE_SERVICE_API_KEY\` in a browser bundle.
511
+
512
+ When the handler looks right locally, \`gf deploy\` the same tree.
513
+
514
+ ## Agent actions
515
+
516
+ Inspect: \`workspace_context\`, \`docs_get\` id \`coderunner\`, \`suggest_cli\`.
517
+ Act: \`gf deploy\`, \`gf coderunner run|status\`. MCP does not deploy.
518
+ `.trim();
519
+
520
+ // src/shell-guide.ts
521
+ var SHELL_APP_GUIDE = `
522
+ # Shell federated apps
523
+
524
+ A Groundfloor **App** (app_kind=shell_federated) is a Module Federation remote
525
+ loaded by the workspace Shell. It is **not** a Coderunner. Do not \`gf deploy\`.
526
+
527
+ ## Required path (do not invent a Vite app)
528
+
529
+ Agents must not scaffold App.tsx, vite.config.ts, or BrowserRouter from memory.
530
+ A from-scratch remote 404s in Shell (missing sibling chunks) or crashes
531
+ (extra Router). Federation \`name\` must be federationRemoteName(slug)
532
+ (\`hello-world\` \u2192 \`hello_world\`).
533
+
534
+ \`\`\`bash
535
+ gf apps create --name "Hello World" --slug hello-world --kind shell_federated
536
+ gf apps init --slug hello-world
537
+ cd hello-world
538
+ npm install
539
+ # edit src/ inside the kit only
540
+ npm run release
541
+ gf apps publish --path release.zip
542
+ \`\`\`
543
+
544
+ \`npm run release\` packs **all** of dist/assets (remoteEntry.js + hashed
545
+ \`__federation_*.js\` + CSS) at the zip root. Do not zip \`dist/\`. Do not
546
+ upload a lone \`remoteEntry.js\`. \`gf apps publish\` rejects incomplete zips.
547
+
548
+ ## suggest_cli
549
+
550
+ - init_app \u2192 \`gf apps init --slug <portal-slug>\`
551
+ - publish_app \u2192 init + npm install + npm run release + gf apps publish --path release.zip
552
+ `;
553
+
383
554
  // src/docs.ts
384
555
  var DOCS_ORIGIN = "https://docs.groundfloor.cloud";
385
556
  var DOC_CATALOG = [
@@ -387,22 +558,36 @@ var DOC_CATALOG = [
387
558
  id: "agents",
388
559
  title: "Deploy with agents",
389
560
  path: "/docs/customer-portal/agents",
390
- summary: "How agents should deploy Coderunner workloads and init/publish Shell apps with gf / MCP."
561
+ 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."
391
562
  },
392
563
  {
393
564
  id: "developers",
394
565
  title: "Developers (gf CLI)",
395
566
  path: "/docs/customer-portal/developers",
396
- summary: "gf login, workspaces, deploy, secrets, files, apps."
567
+ summary: "gf login, workspaces, deploy, secrets, files, apps. Coderunner types: function, job, schedule, service (also called Deployment)."
397
568
  },
398
569
  {
399
570
  id: "coderunner",
400
571
  title: "Coderunner",
401
572
  path: "/docs/customer-portal/coderunner",
402
- summary: "Functions, jobs, schedules, services \u2014 not Apps."
573
+ summary: "Workload types (function, job, schedule, service/Deployment), Dockerfile for services, Authentication before create, local live data, gf deploy.",
574
+ embedded: CODERUNNER_GUIDE
575
+ },
576
+ {
577
+ id: "coderunner-overview",
578
+ title: "Coderunner overview",
579
+ path: "/docs/integrations/coderunner/overview",
580
+ summary: "Architecture and lifecycle. Prefer docs_get id coderunner for type usage, Dockerfile, and auth prerequisites.",
581
+ embedded: CODERUNNER_GUIDE
403
582
  },
404
583
  {
405
584
  id: "authentication",
585
+ title: "Workspace Authentication",
586
+ path: "/docs/customer-portal/authentication",
587
+ summary: "Workspace site auth (Groundfloor or external) must be active before creating a Coderunner. Separate from portal Members & Roles."
588
+ },
589
+ {
590
+ id: "api-authentication",
406
591
  title: "API authentication",
407
592
  path: "/docs/api/authentication",
408
593
  summary: "Control Plane Bearer tokens and workspace identity."
@@ -411,7 +596,8 @@ var DOC_CATALOG = [
411
596
  id: "shell",
412
597
  title: "Shell federated apps",
413
598
  path: "/docs/integrations/shell",
414
- summary: "Product Apps vs Coderunner deploy."
599
+ 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.",
600
+ embedded: SHELL_APP_GUIDE
415
601
  }
416
602
  ];
417
603
  function docUrl(entry) {
@@ -421,7 +607,7 @@ function findDoc(query) {
421
607
  const q = query.trim().toLowerCase();
422
608
  if (!q) return DOC_CATALOG;
423
609
  return DOC_CATALOG.filter((d) => {
424
- const hay = `${d.id} ${d.title} ${d.summary} ${d.path}`.toLowerCase();
610
+ const hay = `${d.id} ${d.title} ${d.summary} ${d.path} ${d.embedded ?? ""}`.toLowerCase();
425
611
  return q.split(/\s+/).every((part) => hay.includes(part));
426
612
  });
427
613
  }
@@ -432,6 +618,15 @@ function htmlToText(html) {
432
618
  var MAX_CHARS = 12e3;
433
619
  async function fetchDocPage(entry) {
434
620
  const url = docUrl(entry);
621
+ if (entry.embedded?.trim()) {
622
+ return {
623
+ url,
624
+ title: entry.title,
625
+ text: entry.embedded.trim(),
626
+ truncated: false,
627
+ source: "embedded"
628
+ };
629
+ }
435
630
  const res = await fetch(url, {
436
631
  headers: { Accept: "text/html", "User-Agent": "groundfloor-mcp/0.1" },
437
632
  redirect: "follow"
@@ -444,7 +639,13 @@ async function fetchDocPage(entry) {
444
639
  const truncated = body.length > MAX_CHARS;
445
640
  if (truncated) body = `${body.slice(0, MAX_CHARS)}
446
641
  \u2026`;
447
- return { url, title: entry.title, text: body, truncated };
642
+ return {
643
+ url,
644
+ title: entry.title,
645
+ text: body,
646
+ truncated,
647
+ source: "docs.groundfloor.cloud"
648
+ };
448
649
  }
449
650
 
450
651
  // src/playbook.ts
@@ -467,7 +668,7 @@ function suggestCli(intent, args) {
467
668
  case "deploy":
468
669
  return {
469
670
  command: name ? `gf deploy -n ${name}${ws}` : `gf deploy${ws}`,
470
- why: "Package the current (or --path) folder and deploy as a Coderunner. Do not create an App for this.",
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 needs package.json scripts.start. Do not create an App for this.",
471
672
  docs: "https://docs.groundfloor.cloud/docs/customer-portal/coderunner"
472
673
  };
473
674
  case "run":
@@ -486,14 +687,14 @@ function suggestCli(intent, args) {
486
687
  const slug = args?.slug?.trim();
487
688
  return {
488
689
  command: slug ? `gf apps init --slug ${slug}` : "gf apps init --slug <portal-slug>",
489
- 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.",
690
+ 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.",
490
691
  docs: "https://docs.groundfloor.cloud/docs/integrations/shell"
491
692
  };
492
693
  }
493
694
  case "publish_app":
494
695
  return {
495
696
  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",
496
- 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.",
697
+ 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.",
497
698
  docs: "https://docs.groundfloor.cloud/docs/integrations/shell"
498
699
  };
499
700
  case "secrets":
@@ -555,7 +756,7 @@ async function resolveCoderunnerId(apiUrl, workspaceId, idOrSlug) {
555
756
  }
556
757
  var server = new McpServer({
557
758
  name: "groundfloor",
558
- version: "0.1.0"
759
+ version: "0.1.3"
559
760
  });
560
761
  server.tool(
561
762
  "whoami",
@@ -582,7 +783,7 @@ server.tool(
582
783
  );
583
784
  server.tool(
584
785
  "workspace_context",
585
- "Live context for the selected workspace: session, coderunners, apps, dataplane, and suggested gf commands. This is how the agent knows *what is here*; use docs_get for *how to do it*.",
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 for the full how-to.",
586
787
  { workspace_id: wsId },
587
788
  async ({ workspace_id }) => {
588
789
  try {
@@ -613,15 +814,35 @@ server.tool(
613
814
  status: app.status
614
815
  })),
615
816
  dataplane,
817
+ coderunner_howto: {
818
+ docs_get: "coderunner",
819
+ before_create: "Enable Administer \u2192 Authentication (mode groundfloor or external, realm active). Mode none cannot mint a Code Runner JWT.",
820
+ types: {
821
+ function: "HTTP handler on PORT/8080. Invoke with gf coderunner run or the Deployment URL. Node requires package.json scripts.start.",
822
+ job: "Run-to-completion worker. Deploy, then gf coderunner run.",
823
+ 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."
825
+ },
826
+ node: 'Bootstrap image uses CMD ["npm","start"]. Missing scripts.start \u2192 deploy Failed (status 4), 8080 connection refused.',
827
+ local_live_data: 'eval "$(gf env)" then call Control Plane vault/files/secrets with Bearer $(gf token). Do not zip .env files.'
828
+ },
829
+ shell_app_howto: {
830
+ docs_get: "shell",
831
+ 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.",
832
+ suggest_cli: "init_app | publish_app"
833
+ },
616
834
  actions: {
617
835
  deploy: suggestCli("deploy"),
618
836
  switch_workspace: suggestCli("switch_workspace"),
619
- run: suggestCli("run")
837
+ run: suggestCli("run"),
838
+ init_app: suggestCli("init_app"),
839
+ publish_app: suggestCli("publish_app")
620
840
  },
621
841
  docs: DOC_CATALOG.map((d) => ({
622
842
  id: d.id,
623
843
  title: d.title,
624
- url: `https://docs.groundfloor.cloud${d.path}`
844
+ url: `https://docs.groundfloor.cloud${d.path}`,
845
+ summary: d.summary
625
846
  }))
626
847
  });
627
848
  } catch (e) {
@@ -819,9 +1040,11 @@ server.tool(
819
1040
  );
820
1041
  server.tool(
821
1042
  "docs_get",
822
- "Fetch a product doc page from docs.groundfloor.cloud (how/when to use CLI). Pass id from docs_list (agents, developers, coderunner, authentication, shell).",
1043
+ "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.",
823
1044
  {
824
- id: z.string().min(1).describe("Catalog id, e.g. agents | developers | coderunner")
1045
+ id: z.string().min(1).describe(
1046
+ "Catalog id, e.g. coderunner | coderunner-overview | agents | developers | authentication | api-authentication | shell"
1047
+ )
825
1048
  },
826
1049
  async ({ id }) => {
827
1050
  try {
@@ -841,7 +1064,7 @@ server.tool(
841
1064
  );
842
1065
  server.tool(
843
1066
  "suggest_cli",
844
- "Return the gf command the developer (or agent terminal) should run. MCP does not execute mutations.",
1067
+ "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.",
845
1068
  {
846
1069
  intent: z.enum([
847
1070
  "login",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundfloorcloud/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Groundfloor MCP — inspect workspace state and docs; take actions with the gf CLI.",
5
5
  "type": "module",
6
6
  "bin": {