@groundfloorcloud/mcp 0.1.9 → 0.1.12
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/dist/index.js +111 -15
- package/package.json +2 -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
|
|
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,34 @@ 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
|
+
Cache the minted JWT for its \`expires_in\` (refresh at ~80%). Never mint per
|
|
703
|
+
request: \`POST \u2026/m2m/token\` is rate-limited per client and per IP (default
|
|
704
|
+
20/minute each) and returns **429** when exceeded. Treat 429/401 from the mint
|
|
705
|
+
call as retryable, not a crash.
|
|
706
|
+
|
|
707
|
+
An M2M client can also read secret **values** directly at runtime instead
|
|
708
|
+
of (or alongside) \`--secret\` deploy-time injection: grant it the
|
|
709
|
+
**\`secrets:read\`** scope (same Portal **Administer \u2192 M2M tokens** screen,
|
|
710
|
+
per-client toggle), then call
|
|
711
|
+
\`GET {CONTROLPLANE_URL}/v1/workspaces/{workspace_id}/secrets/{key}\` with
|
|
712
|
+
\`Authorization: Bearer\` from \`gf m2m token\` locally, or the deployed
|
|
713
|
+
workload's injected \`GROUNDFLOOR_M2M_CLIENT_ID\`/\`GROUNDFLOOR_M2M_CLIENT_SECRET\`
|
|
714
|
+
exchanged the same way. This path is:
|
|
715
|
+
|
|
716
|
+
- **Read-only** \u2014 \`secrets:read\` never grants \`upsert\`/\`delete\`, regardless
|
|
717
|
+
of the client's writer relation on Vault/Files.
|
|
718
|
+
- **Live** \u2014 a rotated secret is visible on your next call, no redeploy
|
|
719
|
+
needed. \`--secret\` injection only updates on the next deploy.
|
|
720
|
+
- **Workspace-wide, not per-key or per-environment** \u2014 granting the scope
|
|
721
|
+
exposes every secret in the workspace across all environments to that
|
|
722
|
+
client's code, same class of trade as the existing Vault/Files writer
|
|
723
|
+
grant. Not a fit for isolating one secret to one Coderunner.
|
|
724
|
+
|
|
725
|
+
Prefer it over \`--secret\` injection when you want live rotation and can
|
|
726
|
+
do the fetch yourself; keep using \`--secret\` injection for SDKs that only
|
|
727
|
+
read a literal \`process.env\` value. Either way, cache the revealed value
|
|
728
|
+
in your process rather than calling \`reveal\` on every request.
|
|
729
|
+
|
|
673
730
|
## Agent actions
|
|
674
731
|
|
|
675
732
|
Inspect: \`workspace_context\`, \`docs_get\` id \`coderunner\` or \`coderunner-runtime\`, \`suggest_cli\`.
|
|
@@ -848,6 +905,14 @@ database only if you need a different engine or workspace-owned isolation
|
|
|
848
905
|
(\`docs_get\` id \`databases\`). Local UI without live vault:
|
|
849
906
|
\`npm run dev:fixtures\`.
|
|
850
907
|
|
|
908
|
+
Detail routes (e.g. \`records/:id\`): once the record loads, call
|
|
909
|
+
\`setBreadcrumbLabel(record.name)\` from the kit's \`src/lib/shellBridge.ts\`
|
|
910
|
+
(wraps \`window.__GF_SET_BREADCRUMB_LABEL__(location.pathname, label)\`; no-op
|
|
911
|
+
outside Shell) so the breadcrumb does not show the raw id. Give that route
|
|
912
|
+
\`hidden: true\` in the manifest so it stays out of the sidebar nav but still
|
|
913
|
+
routes (omit it or use \`false\` otherwise \u2014 never \`null\`). \`visibility:
|
|
914
|
+
"public"\` makes a route reachable without login.
|
|
915
|
+
|
|
851
916
|
## suggest_cli
|
|
852
917
|
|
|
853
918
|
- init_app \u2192 \`gf apps init --slug <portal-slug>\`
|
|
@@ -872,6 +937,10 @@ Pass \`engine\` explicitly \u2014 don't rely on Dataplane's default connector.
|
|
|
872
937
|
\`"postgresql"\` is the platform default (shared, no deploy step);
|
|
873
938
|
\`"arangodb"\` is also available the same way.
|
|
874
939
|
|
|
940
|
+
\`id\` and \`tenant_id\` are reserved field names on Postgres (and any SQL-shaped
|
|
941
|
+
engine) \u2014 Dataplane adds them automatically as system columns on every table.
|
|
942
|
+
Declaring either as a property is a **400**, not a 500. Not reserved on ArangoDB.
|
|
943
|
+
|
|
875
944
|
Example:
|
|
876
945
|
|
|
877
946
|
\`\`\`json
|
|
@@ -934,6 +1003,18 @@ Do not \`--env SENDGRID_SECRET=\u2026\`. That is **422**.
|
|
|
934
1003
|
\`gf secrets set SENDGRID_SECRET\` then \`gf deploy --secret SENDGRID_SECRET\`.
|
|
935
1004
|
`.trim();
|
|
936
1005
|
|
|
1006
|
+
// src/version.ts
|
|
1007
|
+
import { readFileSync } from "fs";
|
|
1008
|
+
import { dirname, join } from "path";
|
|
1009
|
+
import { fileURLToPath } from "url";
|
|
1010
|
+
var moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
1011
|
+
function readPackageVersion() {
|
|
1012
|
+
const pkgPath = join(moduleDir, "..", "package.json");
|
|
1013
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1014
|
+
return pkg.version;
|
|
1015
|
+
}
|
|
1016
|
+
var GF_MCP_VERSION = readPackageVersion();
|
|
1017
|
+
|
|
937
1018
|
// src/docs.ts
|
|
938
1019
|
var DOCS_ORIGIN = "https://docs.groundfloor.cloud";
|
|
939
1020
|
var DOC_CATALOG = [
|
|
@@ -954,21 +1035,24 @@ var DOC_CATALOG = [
|
|
|
954
1035
|
title: "Coderunner",
|
|
955
1036
|
path: "/docs/customer-portal/coderunner",
|
|
956
1037
|
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
|
|
1038
|
+
embedded: CODERUNNER_GUIDE,
|
|
1039
|
+
lastUpdated: "2026-09-24"
|
|
958
1040
|
},
|
|
959
1041
|
{
|
|
960
1042
|
id: "coderunner-runtime",
|
|
961
1043
|
title: "Coderunner runtime contract",
|
|
962
1044
|
path: "/docs/customer-portal/coderunner",
|
|
963
1045
|
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
|
|
1046
|
+
embedded: CODERUNNER_RUNTIME_CONTRACT,
|
|
1047
|
+
lastUpdated: "2026-09-23"
|
|
965
1048
|
},
|
|
966
1049
|
{
|
|
967
1050
|
id: "coderunner-overview",
|
|
968
1051
|
title: "Coderunner overview",
|
|
969
1052
|
path: "/docs/integrations/coderunner/overview",
|
|
970
1053
|
summary: "Architecture and lifecycle. Prefer docs_get id coderunner for type usage, Dockerfile, and create prerequisites.",
|
|
971
|
-
embedded: CODERUNNER_GUIDE
|
|
1054
|
+
embedded: CODERUNNER_GUIDE,
|
|
1055
|
+
lastUpdated: "2026-09-24"
|
|
972
1056
|
},
|
|
973
1057
|
{
|
|
974
1058
|
id: "authentication",
|
|
@@ -986,7 +1070,7 @@ var DOC_CATALOG = [
|
|
|
986
1070
|
id: "m2m",
|
|
987
1071
|
title: "M2M tokens",
|
|
988
1072
|
path: "/docs/customer-portal/m2m-tokens",
|
|
989
|
-
summary: "Confidential client secret \u2192 short Control Plane JWT (typ=m2m). Attach on Coderunner or gf m2m token. Never DATAPLANE_SERVICE_API_KEY. v1 is Team Writer, no collection ACL."
|
|
1073
|
+
summary: "Confidential client secret \u2192 short Control Plane JWT (typ=m2m). Attach on Coderunner or gf m2m token. Never DATAPLANE_SERVICE_API_KEY. v1 is Team Writer, no collection ACL. Secrets only with the opt-in secrets:read scope (read-only). Cache the token; minting is rate-limited per client and IP."
|
|
990
1074
|
},
|
|
991
1075
|
{
|
|
992
1076
|
id: "api-authentication",
|
|
@@ -999,42 +1083,48 @@ var DOC_CATALOG = [
|
|
|
999
1083
|
title: "Shell federated apps",
|
|
1000
1084
|
path: "/docs/integrations/shell",
|
|
1001
1085
|
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
|
|
1086
|
+
embedded: SHELL_APP_GUIDE,
|
|
1087
|
+
lastUpdated: "2026-09-24"
|
|
1003
1088
|
},
|
|
1004
1089
|
{
|
|
1005
1090
|
id: "design-standard",
|
|
1006
1091
|
title: "Shell app design standard",
|
|
1007
1092
|
path: "/docs/integrations/shell/design-standard",
|
|
1008
1093
|
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
|
|
1094
|
+
embedded: DESIGN_STANDARD_GUIDE,
|
|
1095
|
+
lastUpdated: "2026-09-04"
|
|
1010
1096
|
},
|
|
1011
1097
|
{
|
|
1012
1098
|
id: "css-and-styling",
|
|
1013
1099
|
title: "CSS and styling",
|
|
1014
1100
|
path: "/docs/integrations/shell/css-and-styling",
|
|
1015
1101
|
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
|
|
1102
|
+
embedded: CSS_AND_STYLING_GUIDE,
|
|
1103
|
+
lastUpdated: "2026-09-04"
|
|
1017
1104
|
},
|
|
1018
1105
|
{
|
|
1019
1106
|
id: "databases",
|
|
1020
1107
|
title: "Databases",
|
|
1021
1108
|
path: "/docs/customer-portal/databases",
|
|
1022
1109
|
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
|
|
1110
|
+
embedded: DATABASES_GUIDE,
|
|
1111
|
+
lastUpdated: "2026-09-22"
|
|
1024
1112
|
},
|
|
1025
1113
|
{
|
|
1026
1114
|
id: "vault",
|
|
1027
1115
|
title: "Data Vault",
|
|
1028
1116
|
path: "/docs/api/vault",
|
|
1029
1117
|
summary: "Create collections with JSON Schema, query totals vs limit, ACLs, implicit tables, nested filters, identities vs M2M.",
|
|
1030
|
-
embedded: VAULT_GUIDE
|
|
1118
|
+
embedded: VAULT_GUIDE,
|
|
1119
|
+
lastUpdated: "2026-09-22"
|
|
1031
1120
|
},
|
|
1032
1121
|
{
|
|
1033
1122
|
id: "scheduled-jobs",
|
|
1034
1123
|
title: "Scheduled jobs",
|
|
1035
1124
|
path: "/docs/customer-portal/scheduled-jobs",
|
|
1036
1125
|
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
|
|
1126
|
+
embedded: CODERUNNER_GUIDE,
|
|
1127
|
+
lastUpdated: "2026-09-24"
|
|
1038
1128
|
}
|
|
1039
1129
|
];
|
|
1040
1130
|
function docUrl(entry) {
|
|
@@ -1061,7 +1151,9 @@ async function fetchDocPage(entry) {
|
|
|
1061
1151
|
title: entry.title,
|
|
1062
1152
|
text: entry.embedded.trim(),
|
|
1063
1153
|
truncated: false,
|
|
1064
|
-
source: "embedded"
|
|
1154
|
+
source: "embedded",
|
|
1155
|
+
lastUpdated: entry.lastUpdated ?? null,
|
|
1156
|
+
mcpVersion: GF_MCP_VERSION
|
|
1065
1157
|
};
|
|
1066
1158
|
}
|
|
1067
1159
|
const res = await fetch(url, {
|
|
@@ -1081,7 +1173,9 @@ async function fetchDocPage(entry) {
|
|
|
1081
1173
|
title: entry.title,
|
|
1082
1174
|
text: body,
|
|
1083
1175
|
truncated,
|
|
1084
|
-
source: "docs.groundfloor.cloud"
|
|
1176
|
+
source: "docs.groundfloor.cloud",
|
|
1177
|
+
lastUpdated: null,
|
|
1178
|
+
mcpVersion: GF_MCP_VERSION
|
|
1085
1179
|
};
|
|
1086
1180
|
}
|
|
1087
1181
|
|
|
@@ -1218,7 +1312,7 @@ async function resolveCoderunnerId(apiUrl, workspaceId, idOrSlug) {
|
|
|
1218
1312
|
}
|
|
1219
1313
|
var server = new McpServer({
|
|
1220
1314
|
name: "groundfloor",
|
|
1221
|
-
version:
|
|
1315
|
+
version: GF_MCP_VERSION
|
|
1222
1316
|
});
|
|
1223
1317
|
server.tool(
|
|
1224
1318
|
"whoami",
|
|
@@ -1599,11 +1693,13 @@ server.tool(
|
|
|
1599
1693
|
const hits = findDoc(query ?? "");
|
|
1600
1694
|
return text({
|
|
1601
1695
|
origin: "https://docs.groundfloor.cloud",
|
|
1696
|
+
mcpVersion: GF_MCP_VERSION,
|
|
1602
1697
|
docs: hits.map((d) => ({
|
|
1603
1698
|
id: d.id,
|
|
1604
1699
|
title: d.title,
|
|
1605
1700
|
url: `https://docs.groundfloor.cloud${d.path}`,
|
|
1606
|
-
summary: d.summary
|
|
1701
|
+
summary: d.summary,
|
|
1702
|
+
lastUpdated: d.lastUpdated ?? null
|
|
1607
1703
|
}))
|
|
1608
1704
|
});
|
|
1609
1705
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groundfloorcloud/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Groundfloor MCP — inspect workspace state and docs; take actions with the gf CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^20",
|
|
41
|
+
"esbuild": "^0.27.7",
|
|
41
42
|
"tsup": "^8.3.0",
|
|
42
43
|
"typescript": "^5"
|
|
43
44
|
},
|