@groundfloorcloud/mcp 0.1.9 → 0.1.11

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/dist/index.js +97 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -473,6 +473,22 @@ server.listen(port, "0.0.0.0", () => {
473
473
 
474
474
  Offer GET \`/\` or \`/health\` for the probe. Sample: \`samples/hello-fn/\`.
475
475
 
476
+ ## Known issue: bootstrap has been reported to skip \`npm start\`
477
+
478
+ Reported 2026-09-22 against a \`function\` deploy with no Dockerfile, **unresolved as of this
479
+ writing**: instead of running \`npm start\` per the contract above, the platform's own
480
+ \`/app/main.js\` ran \`require('./handler')\` \u2014 a file/module contract this document does not
481
+ describe \u2014 even though \`scripts.start\` and the entrypoint were present and correct. Symptom in
482
+ \`gf coderunner logs -c <id>\`: \`Cannot find module './handler'\` (\`MODULE_NOT_FOUND\`), repeated
483
+ probe \`127.0.0.1:8080\` connection refused, deployment **Failed (status 4)**. Whether \`job\` and
484
+ \`schedule\` share this bootstrap path and are equally affected has not been confirmed either way \u2014
485
+ treat the workaround below as precautionary for those types too if you hit the same signature.
486
+
487
+ **Workaround:** add your own root \`Dockerfile\` (e.g. \`CMD ["npm", "start"]\`) and keep your
488
+ existing \`workload_type\` \u2014 a Dockerfile bypasses the injected bootstrap entirely regardless of
489
+ type (see "Node (with Dockerfile)" below), so this sidesteps the bug without changing how the
490
+ workload is invoked.
491
+
476
492
  ## Node (with Dockerfile)
477
493
 
478
494
  You own \`CMD\`. If it is \`npm start\`, you still need \`scripts.start\`.
@@ -556,11 +572,17 @@ Pick **one** type per coderunner. Changing type later means a new coderunner.
556
572
  probe \`127.0.0.1:8080\` connection refused, deployment **Failed (status 4)**.
557
573
  - Python: process must bind 0.0.0.0:\${PORT:-8080}.
558
574
  - After deploy: \`gf coderunner run -c <slug> --payload '{"hello":"world"}'\`.
575
+ - \u26A0\uFE0F See "Known issue" in \`docs_get\` id \`coderunner-runtime\` if \`gf coderunner logs\` shows
576
+ \`Cannot find module './handler'\` despite a correct \`scripts.start\` \u2014 add a Dockerfile as the
577
+ workaround, no type change needed.
559
578
 
560
579
  ### Job
561
580
 
562
581
  - Same package rules as function. No need to serve forever; exit 0 on success.
563
582
  - Deploy, then start a run (\`gf coderunner run\`). Check Process Log.
583
+ - \u26A0\uFE0F Not confirmed whether the no-Dockerfile bootstrap bug in \`docs_get\` id \`coderunner-runtime\`
584
+ ("Known issue") also hits \`job\` \u2014 if a run never starts and logs show the same
585
+ \`MODULE_NOT_FOUND: './handler'\`, apply the same Dockerfile workaround.
564
586
 
565
587
  ### Schedule
566
588
 
@@ -570,6 +592,8 @@ Pick **one** type per coderunner. Changing type later means a new coderunner.
570
592
  - GET/list/status expose top-level \`workload_type\` and \`schedule\`.
571
593
  \`gf coderunner status -c <slug>\` prints type, schedule, last_job.
572
594
  - No public invoke URL. Debug with \`gf coderunner logs\`, not curl.
595
+ - \u26A0\uFE0F Same caveat as Job: unconfirmed whether the bootstrap "Known issue" affects scheduled runs;
596
+ same Dockerfile workaround if you see the identical crash signature.
573
597
 
574
598
  ### Service (Deployment)
575
599
 
@@ -661,8 +685,13 @@ When the handler looks right locally, \`gf deploy\` the same tree.
661
685
  Do not pass credential-shaped keys as \`--env KEY=VALUE\` (\`*_SECRET\`,
662
686
  \`*_API_KEY\`, \`DATABASE_URL\`, \u2026). Control Plane returns **422**.
663
687
  \`gf secrets set KEY\` then \`gf deploy --secret KEY\` (inject by reference).
688
+ This requires \`CODERUNNER_INJECT_ENV_SECRETS\` enabled for the workspace \u2014
689
+ **off by default**. If \`--secret\` 422s naming that setting, either ask your
690
+ platform operator to enable it, or read the secret live via M2M
691
+ \`secrets:read\` instead (below) rather than falling back to plaintext
692
+ \`--env\`.
664
693
 
665
- ## M2M tokens (Vault and Files)
694
+ ## M2M tokens (Vault, Files, and Secrets)
666
695
 
667
696
  New Coderunners cannot call Vault or Files until an M2M client is attached
668
697
  and the workload is redeployed. Create the client in Portal **Administer \u2192
@@ -670,6 +699,29 @@ M2M tokens**, attach it on the Coderunner card, then deploy. v1 M2M is Team
670
699
  Writer on that workspace (full Vault skip-ACL + Files). Grant site users on
671
700
  **Administer \u2192 Access**. Never inject a Dataplane key.
672
701
 
702
+ An M2M client can also read secret **values** directly at runtime instead
703
+ of (or alongside) \`--secret\` deploy-time injection: grant it the
704
+ **\`secrets:read\`** scope (same Portal **Administer \u2192 M2M tokens** screen,
705
+ per-client toggle), then call
706
+ \`GET {CONTROLPLANE_URL}/v1/workspaces/{workspace_id}/secrets/{key}\` with
707
+ \`Authorization: Bearer\` from \`gf m2m token\` locally, or the deployed
708
+ workload's injected \`GROUNDFLOOR_M2M_CLIENT_ID\`/\`GROUNDFLOOR_M2M_CLIENT_SECRET\`
709
+ exchanged the same way. This path is:
710
+
711
+ - **Read-only** \u2014 \`secrets:read\` never grants \`upsert\`/\`delete\`, regardless
712
+ of the client's writer relation on Vault/Files.
713
+ - **Live** \u2014 a rotated secret is visible on your next call, no redeploy
714
+ needed. \`--secret\` injection only updates on the next deploy.
715
+ - **Workspace-wide, not per-key or per-environment** \u2014 granting the scope
716
+ exposes every secret in the workspace across all environments to that
717
+ client's code, same class of trade as the existing Vault/Files writer
718
+ grant. Not a fit for isolating one secret to one Coderunner.
719
+
720
+ Prefer it over \`--secret\` injection when you want live rotation and can
721
+ do the fetch yourself; keep using \`--secret\` injection for SDKs that only
722
+ read a literal \`process.env\` value. Either way, cache the revealed value
723
+ in your process rather than calling \`reveal\` on every request.
724
+
673
725
  ## Agent actions
674
726
 
675
727
  Inspect: \`workspace_context\`, \`docs_get\` id \`coderunner\` or \`coderunner-runtime\`, \`suggest_cli\`.
@@ -872,6 +924,10 @@ Pass \`engine\` explicitly \u2014 don't rely on Dataplane's default connector.
872
924
  \`"postgresql"\` is the platform default (shared, no deploy step);
873
925
  \`"arangodb"\` is also available the same way.
874
926
 
927
+ \`id\` and \`tenant_id\` are reserved field names on Postgres (and any SQL-shaped
928
+ engine) \u2014 Dataplane adds them automatically as system columns on every table.
929
+ Declaring either as a property is a **400**, not a 500. Not reserved on ArangoDB.
930
+
875
931
  Example:
876
932
 
877
933
  \`\`\`json
@@ -934,6 +990,18 @@ Do not \`--env SENDGRID_SECRET=\u2026\`. That is **422**.
934
990
  \`gf secrets set SENDGRID_SECRET\` then \`gf deploy --secret SENDGRID_SECRET\`.
935
991
  `.trim();
936
992
 
993
+ // src/version.ts
994
+ import { readFileSync } from "fs";
995
+ import { dirname, join } from "path";
996
+ import { fileURLToPath } from "url";
997
+ var moduleDir = dirname(fileURLToPath(import.meta.url));
998
+ function readPackageVersion() {
999
+ const pkgPath = join(moduleDir, "..", "package.json");
1000
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
1001
+ return pkg.version;
1002
+ }
1003
+ var GF_MCP_VERSION = readPackageVersion();
1004
+
937
1005
  // src/docs.ts
938
1006
  var DOCS_ORIGIN = "https://docs.groundfloor.cloud";
939
1007
  var DOC_CATALOG = [
@@ -954,21 +1022,24 @@ var DOC_CATALOG = [
954
1022
  title: "Coderunner",
955
1023
  path: "/docs/customer-portal/coderunner",
956
1024
  summary: "Workload types (function, job, schedule, service/Deployment), runtime contract (scripts.start, 0.0.0.0:8080), Dockerfile for services, Coderunner identities for Vault, App authentication optional, local live data, gf deploy.",
957
- embedded: CODERUNNER_GUIDE
1025
+ embedded: CODERUNNER_GUIDE,
1026
+ lastUpdated: "2026-09-23"
958
1027
  },
959
1028
  {
960
1029
  id: "coderunner-runtime",
961
1030
  title: "Coderunner runtime contract",
962
1031
  path: "/docs/customer-portal/coderunner",
963
1032
  summary: "What the platform expects your code to do: Node bootstrap (npm start, PORT/8080, bind 0.0.0.0), Python bind, service Dockerfile.",
964
- embedded: CODERUNNER_RUNTIME_CONTRACT
1033
+ embedded: CODERUNNER_RUNTIME_CONTRACT,
1034
+ lastUpdated: "2026-09-23"
965
1035
  },
966
1036
  {
967
1037
  id: "coderunner-overview",
968
1038
  title: "Coderunner overview",
969
1039
  path: "/docs/integrations/coderunner/overview",
970
1040
  summary: "Architecture and lifecycle. Prefer docs_get id coderunner for type usage, Dockerfile, and create prerequisites.",
971
- embedded: CODERUNNER_GUIDE
1041
+ embedded: CODERUNNER_GUIDE,
1042
+ lastUpdated: "2026-09-23"
972
1043
  },
973
1044
  {
974
1045
  id: "authentication",
@@ -999,42 +1070,48 @@ var DOC_CATALOG = [
999
1070
  title: "Shell federated apps",
1000
1071
  path: "/docs/integrations/shell",
1001
1072
  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.",
1002
- embedded: SHELL_APP_GUIDE
1073
+ embedded: SHELL_APP_GUIDE,
1074
+ lastUpdated: "2026-09-22"
1003
1075
  },
1004
1076
  {
1005
1077
  id: "design-standard",
1006
1078
  title: "Shell app design standard",
1007
1079
  path: "/docs/integrations/shell/design-standard",
1008
1080
  summary: "Visual rules for Shell apps: grey ground, flat controls, Geist, one brand accent from theme.primaryColor, DataGrid, PhaseChevron. Use the kit; do not restyle to stock Tailwind.",
1009
- embedded: DESIGN_STANDARD_GUIDE
1081
+ embedded: DESIGN_STANDARD_GUIDE,
1082
+ lastUpdated: "2026-09-04"
1010
1083
  },
1011
1084
  {
1012
1085
  id: "css-and-styling",
1013
1086
  title: "CSS and styling",
1014
1087
  path: "/docs/integrations/shell/css-and-styling",
1015
1088
  summary: "Tailwind is the default. Own CSS framework (Bootstrap, MUI, \u2026) uses FederatedStyleRoot and Shadow DOM \u2014 do not load it globally onto the Shell page.",
1016
- embedded: CSS_AND_STYLING_GUIDE
1089
+ embedded: CSS_AND_STYLING_GUIDE,
1090
+ lastUpdated: "2026-09-04"
1017
1091
  },
1018
1092
  {
1019
1093
  id: "databases",
1020
1094
  title: "Databases",
1021
1095
  path: "/docs/customer-portal/databases",
1022
1096
  summary: "Dataplane is not a database. Postgres (shared, platform-managed) is the default, ready through Dataplane with no deploy step. ArangoDB is also ready the same way. A dedicated database you deploy and own is a separate, heavier path.",
1023
- embedded: DATABASES_GUIDE
1097
+ embedded: DATABASES_GUIDE,
1098
+ lastUpdated: "2026-09-22"
1024
1099
  },
1025
1100
  {
1026
1101
  id: "vault",
1027
1102
  title: "Data Vault",
1028
1103
  path: "/docs/api/vault",
1029
1104
  summary: "Create collections with JSON Schema, query totals vs limit, ACLs, implicit tables, nested filters, identities vs M2M.",
1030
- embedded: VAULT_GUIDE
1105
+ embedded: VAULT_GUIDE,
1106
+ lastUpdated: "2026-09-22"
1031
1107
  },
1032
1108
  {
1033
1109
  id: "scheduled-jobs",
1034
1110
  title: "Scheduled jobs",
1035
1111
  path: "/docs/customer-portal/scheduled-jobs",
1036
1112
  summary: "workload_type=schedule plus cron. gf coderunner status shows type/schedule/last_job. Use Process Log, not a public URL.",
1037
- embedded: CODERUNNER_GUIDE
1113
+ embedded: CODERUNNER_GUIDE,
1114
+ lastUpdated: "2026-09-23"
1038
1115
  }
1039
1116
  ];
1040
1117
  function docUrl(entry) {
@@ -1061,7 +1138,9 @@ async function fetchDocPage(entry) {
1061
1138
  title: entry.title,
1062
1139
  text: entry.embedded.trim(),
1063
1140
  truncated: false,
1064
- source: "embedded"
1141
+ source: "embedded",
1142
+ lastUpdated: entry.lastUpdated ?? null,
1143
+ mcpVersion: GF_MCP_VERSION
1065
1144
  };
1066
1145
  }
1067
1146
  const res = await fetch(url, {
@@ -1081,7 +1160,9 @@ async function fetchDocPage(entry) {
1081
1160
  title: entry.title,
1082
1161
  text: body,
1083
1162
  truncated,
1084
- source: "docs.groundfloor.cloud"
1163
+ source: "docs.groundfloor.cloud",
1164
+ lastUpdated: null,
1165
+ mcpVersion: GF_MCP_VERSION
1085
1166
  };
1086
1167
  }
1087
1168
 
@@ -1218,7 +1299,7 @@ async function resolveCoderunnerId(apiUrl, workspaceId, idOrSlug) {
1218
1299
  }
1219
1300
  var server = new McpServer({
1220
1301
  name: "groundfloor",
1221
- version: "0.1.7"
1302
+ version: GF_MCP_VERSION
1222
1303
  });
1223
1304
  server.tool(
1224
1305
  "whoami",
@@ -1599,11 +1680,13 @@ server.tool(
1599
1680
  const hits = findDoc(query ?? "");
1600
1681
  return text({
1601
1682
  origin: "https://docs.groundfloor.cloud",
1683
+ mcpVersion: GF_MCP_VERSION,
1602
1684
  docs: hits.map((d) => ({
1603
1685
  id: d.id,
1604
1686
  title: d.title,
1605
1687
  url: `https://docs.groundfloor.cloud${d.path}`,
1606
- summary: d.summary
1688
+ summary: d.summary,
1689
+ lastUpdated: d.lastUpdated ?? null
1607
1690
  }))
1608
1691
  });
1609
1692
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundfloorcloud/mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Groundfloor MCP — inspect workspace state and docs; take actions with the gf CLI.",
5
5
  "type": "module",
6
6
  "bin": {