@lukoweb/apitogo 0.2.57 → 0.2.59
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/cli/cli.js +117 -12
- package/dist/cli/worker.js +4 -1
- package/docs/cli/overview.md +9 -10
- package/docs/cli/setup.md +2 -2
- package/docs/configuration/portal.md +2 -1
- package/docs/mcp/go-live.md +44 -72
- package/docs/mcp/overview.md +39 -57
- package/docs/mcp/setup.md +169 -173
- package/docs/mcp/shape.md +77 -77
- package/docs/mcp/tools.md +52 -96
- package/package.json +1 -1
- package/src/config/v2/gateway-request.ts +5 -2
- package/src/config/v2/index.ts +1 -0
- package/src/config/v2/manifest.ts +25 -0
- package/src/config/v2/resolve.ts +5 -1
- package/src/config/v2/schema.ts +18 -5
- package/src/vite/prerender/utils.ts +89 -76
- package/src/vite/prerender/worker.ts +2 -1
package/dist/cli/cli.js
CHANGED
|
@@ -5931,11 +5931,23 @@ var PortalSchema = z.strictObject({
|
|
|
5931
5931
|
landing: path3.optional(),
|
|
5932
5932
|
/** The footer: a React component file shown under every page except the console's (API-129). */
|
|
5933
5933
|
footer: path3.optional(),
|
|
5934
|
-
/**
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5934
|
+
/**
|
|
5935
|
+
* The logo in the portal header: one image, or one per color scheme (API-129). `dark` is optional; without it the
|
|
5936
|
+
* light image is used in both.
|
|
5937
|
+
*/
|
|
5938
|
+
logo: z.union(
|
|
5939
|
+
[
|
|
5940
|
+
path3,
|
|
5941
|
+
z.strictObject({
|
|
5942
|
+
light: path3,
|
|
5943
|
+
dark: path3.optional(),
|
|
5944
|
+
alt: z.string().optional()
|
|
5945
|
+
})
|
|
5946
|
+
],
|
|
5947
|
+
{
|
|
5948
|
+
error: 'must be an image path or https address, or { "light": "...", "dark": "...", "alt": "..." } (dark and alt are optional)'
|
|
5949
|
+
}
|
|
5950
|
+
).optional(),
|
|
5939
5951
|
requireAuth: z.union([z.boolean(), z.strictObject({ exclude: pathList })], {
|
|
5940
5952
|
error: 'must be true, false or { "exclude": ["/route", ...] }'
|
|
5941
5953
|
}).optional(),
|
|
@@ -6507,7 +6519,11 @@ function resolveConfigV2(config2, configPath) {
|
|
|
6507
6519
|
dark: logoSource(configDir, portal.logo)
|
|
6508
6520
|
} : {
|
|
6509
6521
|
light: logoSource(configDir, portal.logo.light),
|
|
6510
|
-
dark
|
|
6522
|
+
// Without a dark image, the light one is used in both color schemes.
|
|
6523
|
+
dark: logoSource(
|
|
6524
|
+
configDir,
|
|
6525
|
+
portal.logo.dark ?? portal.logo.light
|
|
6526
|
+
),
|
|
6511
6527
|
...portal.logo.alt ? { alt: portal.logo.alt } : {}
|
|
6512
6528
|
}
|
|
6513
6529
|
} : {},
|
|
@@ -7472,6 +7488,8 @@ var ServiceManifestSchema = z3.looseObject({
|
|
|
7472
7488
|
services: z3.looseObject({
|
|
7473
7489
|
portal: z3.looseObject({
|
|
7474
7490
|
url: z3.string().optional(),
|
|
7491
|
+
/** The portal's custom domain once it is ready, such as `https://docs.acme.com` (API-130). */
|
|
7492
|
+
customDomain: z3.string().optional(),
|
|
7475
7493
|
docs: z3.looseObject({ provider: z3.string(), url: z3.string().optional() }).optional(),
|
|
7476
7494
|
console: z3.looseObject({ links: z3.record(z3.string(), z3.string()).optional() }).optional()
|
|
7477
7495
|
}).optional(),
|
|
@@ -7643,6 +7661,20 @@ function withPortalEntry(existing, info) {
|
|
|
7643
7661
|
}
|
|
7644
7662
|
};
|
|
7645
7663
|
}
|
|
7664
|
+
function withPortalCustomDomain(existing, customDomain) {
|
|
7665
|
+
if (!existing) return void 0;
|
|
7666
|
+
const portal = existing.services.portal;
|
|
7667
|
+
if (portal?.customDomain === customDomain) return void 0;
|
|
7668
|
+
if (!portal && customDomain === void 0) return void 0;
|
|
7669
|
+
const { customDomain: _previous, ...rest } = portal ?? {};
|
|
7670
|
+
return {
|
|
7671
|
+
...existing,
|
|
7672
|
+
services: {
|
|
7673
|
+
...existing.services,
|
|
7674
|
+
portal: customDomain === void 0 ? rest : { ...rest, customDomain }
|
|
7675
|
+
}
|
|
7676
|
+
};
|
|
7677
|
+
}
|
|
7646
7678
|
function withGatewayEntry(existing, info) {
|
|
7647
7679
|
const base = existing ?? parseManifest({
|
|
7648
7680
|
schemaVersion: SUPPORTED_MANIFEST_SCHEMA,
|
|
@@ -7922,7 +7954,8 @@ function derivedCorsSites(sites) {
|
|
|
7922
7954
|
};
|
|
7923
7955
|
const found = [
|
|
7924
7956
|
{ origin: originOf(sites.docsUrl), source: "docs" },
|
|
7925
|
-
{ origin: originOf(sites.portalUrl), source: "portal" }
|
|
7957
|
+
{ origin: originOf(sites.portalUrl), source: "portal" },
|
|
7958
|
+
{ origin: originOf(sites.portalCustomDomain), source: "portal" }
|
|
7926
7959
|
].flatMap((site) => site.origin ? [{ ...site, origin: site.origin }] : []);
|
|
7927
7960
|
const seen = /* @__PURE__ */ new Set();
|
|
7928
7961
|
return found.filter((site) => !seen.has(site.origin) && seen.add(site.origin)).sort((a, b) => a.origin < b.origin ? -1 : a.origin > b.origin ? 1 : 0);
|
|
@@ -11166,7 +11199,7 @@ import {
|
|
|
11166
11199
|
// package.json
|
|
11167
11200
|
var package_default = {
|
|
11168
11201
|
name: "@lukoweb/apitogo",
|
|
11169
|
-
version: "0.2.
|
|
11202
|
+
version: "0.2.59",
|
|
11170
11203
|
type: "module",
|
|
11171
11204
|
sideEffects: [
|
|
11172
11205
|
"**/*.css",
|
|
@@ -15720,11 +15753,12 @@ var collectPaths = (resolved, parentPath) => resolved.flatMap((r) => [
|
|
|
15720
15753
|
...r.path !== parentPath ? [r.path] : [],
|
|
15721
15754
|
...collectPaths(r.children, r.path)
|
|
15722
15755
|
]);
|
|
15756
|
+
var prerenderFileFor = (urlPath) => urlPath === "/" ? "/index.html" : urlPath.endsWith("/index") ? `${urlPath}/index.html` : `${urlPath}.html`;
|
|
15723
15757
|
var collectRewrites = (resolved) => resolved.flatMap((r) => [
|
|
15724
15758
|
...r.hasStrippedParams && r.path ? [
|
|
15725
15759
|
{
|
|
15726
15760
|
source: `${r.path}/(.+)`,
|
|
15727
|
-
destination: r.path
|
|
15761
|
+
destination: prerenderFileFor(r.path)
|
|
15728
15762
|
}
|
|
15729
15763
|
] : [],
|
|
15730
15764
|
...collectRewrites(r.children)
|
|
@@ -16549,6 +16583,15 @@ async function isConfigV2(dir) {
|
|
|
16549
16583
|
return false;
|
|
16550
16584
|
}
|
|
16551
16585
|
}
|
|
16586
|
+
async function isLegacyConfig(dir) {
|
|
16587
|
+
let text2;
|
|
16588
|
+
try {
|
|
16589
|
+
text2 = await readFile18(path51.join(dir, APITOGO_CONFIG_FILENAME), "utf8");
|
|
16590
|
+
} catch {
|
|
16591
|
+
return false;
|
|
16592
|
+
}
|
|
16593
|
+
return !/"version"\s*:\s*2\b/.test(text2);
|
|
16594
|
+
}
|
|
16552
16595
|
async function fetchSecretNames(config2, configuredProjectId, args, deps) {
|
|
16553
16596
|
const projectId = configuredProjectId || deps.envProjectId?.trim();
|
|
16554
16597
|
if (args.offline || !deps.listSecrets || !projectId) return {};
|
|
@@ -16579,6 +16622,7 @@ async function addGatewayOnlineRules(report, resolved, args, deps) {
|
|
|
16579
16622
|
deps.gateway,
|
|
16580
16623
|
derivedCorsSites({
|
|
16581
16624
|
portalUrl: manifest?.services.portal?.url,
|
|
16625
|
+
portalCustomDomain: manifest?.services.portal?.customDomain,
|
|
16582
16626
|
docsUrl: resolved.portal?.docs.provider === "external" ? resolved.portal.docs.url : void 0
|
|
16583
16627
|
})
|
|
16584
16628
|
);
|
|
@@ -27162,6 +27206,22 @@ init_invariant();
|
|
|
27162
27206
|
import { createHash as createHash4 } from "node:crypto";
|
|
27163
27207
|
import { readFile as readFile28 } from "node:fs/promises";
|
|
27164
27208
|
init_invariant();
|
|
27209
|
+
|
|
27210
|
+
// src/cli/deploy/custom-domain.ts
|
|
27211
|
+
async function readyCustomDomainOrigin(client, projectId) {
|
|
27212
|
+
if (!client.getCustomDomain) return void 0;
|
|
27213
|
+
let domain;
|
|
27214
|
+
try {
|
|
27215
|
+
domain = await client.getCustomDomain(projectId);
|
|
27216
|
+
} catch {
|
|
27217
|
+
return void 0;
|
|
27218
|
+
}
|
|
27219
|
+
const hostname = domain.hostname?.trim().toLowerCase();
|
|
27220
|
+
if (domain.status?.toLowerCase() !== "ready" || !hostname) return null;
|
|
27221
|
+
return `https://${hostname}`;
|
|
27222
|
+
}
|
|
27223
|
+
|
|
27224
|
+
// src/cli/deploy/gateway.ts
|
|
27165
27225
|
var GatewayDeployError = class extends ApitogoError {
|
|
27166
27226
|
constructor(code, message, developerHint) {
|
|
27167
27227
|
super(message, { developerHint, title: "Cannot deploy the gateway" });
|
|
@@ -27207,6 +27267,15 @@ async function readSpec(gateway) {
|
|
|
27207
27267
|
);
|
|
27208
27268
|
}
|
|
27209
27269
|
}
|
|
27270
|
+
async function recordPortalCustomDomain(resolved, projectId, client) {
|
|
27271
|
+
const origin = await readyCustomDomainOrigin(client, projectId);
|
|
27272
|
+
if (origin === void 0) return;
|
|
27273
|
+
const updated = withPortalCustomDomain(
|
|
27274
|
+
await readManifest(resolved.state),
|
|
27275
|
+
origin ?? void 0
|
|
27276
|
+
);
|
|
27277
|
+
if (updated) await writeManifest(resolved.state, updated);
|
|
27278
|
+
}
|
|
27210
27279
|
async function gatewayDeployInputs(resolved, projectId) {
|
|
27211
27280
|
const gateway = resolved.gateway;
|
|
27212
27281
|
if (!gateway) throw new Error("gateway is not configured");
|
|
@@ -27216,6 +27285,7 @@ async function gatewayDeployInputs(resolved, projectId) {
|
|
|
27216
27285
|
gateway,
|
|
27217
27286
|
derivedCorsSites({
|
|
27218
27287
|
portalUrl: manifest?.services.portal?.url,
|
|
27288
|
+
portalCustomDomain: manifest?.services.portal?.customDomain,
|
|
27219
27289
|
docsUrl: resolved.portal?.docs.provider === "external" ? resolved.portal.docs.url : void 0
|
|
27220
27290
|
})
|
|
27221
27291
|
);
|
|
@@ -27304,6 +27374,7 @@ function rejected(what, error) {
|
|
|
27304
27374
|
throw error;
|
|
27305
27375
|
}
|
|
27306
27376
|
async function deployGateway(resolved, projectId, deps, options = {}) {
|
|
27377
|
+
await recordPortalCustomDomain(resolved, projectId, deps.client);
|
|
27307
27378
|
const { gateway, spec, body, specHash, configHash } = await gatewayDeployInputs(resolved, projectId);
|
|
27308
27379
|
assertDeployable(gateway);
|
|
27309
27380
|
const backend = await backendDeployInputs(gateway, projectId);
|
|
@@ -28118,6 +28189,14 @@ function identityBrandingBody(resolved) {
|
|
|
28118
28189
|
};
|
|
28119
28190
|
}
|
|
28120
28191
|
var IDENTITY_BRANDING_STEP = "branding";
|
|
28192
|
+
var IDENTITY_PORTAL_ADDRESS_STEP = "portal-address";
|
|
28193
|
+
async function customDomainCallbackOf(client, projectId, portalUrl) {
|
|
28194
|
+
const origin = await readyCustomDomainOrigin(client, projectId);
|
|
28195
|
+
if (!origin) return void 0;
|
|
28196
|
+
const callback = `${origin}${PORTAL_CALLBACK_PATH}`;
|
|
28197
|
+
const current = `${portalUrl.replace(/\/+$/, "")}${PORTAL_CALLBACK_PATH}`;
|
|
28198
|
+
return callback.toLowerCase() === current.toLowerCase() ? void 0 : callback;
|
|
28199
|
+
}
|
|
28121
28200
|
function identityInputsHash(projectId, body) {
|
|
28122
28201
|
return stableJson({ projectId, body });
|
|
28123
28202
|
}
|
|
@@ -28282,6 +28361,29 @@ async function deployIdentity(resolved, projectId, deps, options = {}) {
|
|
|
28282
28361
|
}
|
|
28283
28362
|
});
|
|
28284
28363
|
}
|
|
28364
|
+
const customCallback = await customDomainCallbackOf(
|
|
28365
|
+
client,
|
|
28366
|
+
projectId,
|
|
28367
|
+
portalUrl
|
|
28368
|
+
);
|
|
28369
|
+
if (customCallback) {
|
|
28370
|
+
const portalBody = {
|
|
28371
|
+
...body,
|
|
28372
|
+
portalRedirectUris: [...body.portalRedirectUris, customCallback]
|
|
28373
|
+
};
|
|
28374
|
+
steps.push({
|
|
28375
|
+
name: IDENTITY_PORTAL_ADDRESS_STEP,
|
|
28376
|
+
hash: () => identityInputsHash(projectId, portalBody),
|
|
28377
|
+
run: async () => {
|
|
28378
|
+
try {
|
|
28379
|
+
await client.putProjectIdentity(projectId, portalBody);
|
|
28380
|
+
} catch (error) {
|
|
28381
|
+
return rejected2(error);
|
|
28382
|
+
}
|
|
28383
|
+
return { redirectUri: customCallback };
|
|
28384
|
+
}
|
|
28385
|
+
});
|
|
28386
|
+
}
|
|
28285
28387
|
const wanted = normalizedConnections(resolved);
|
|
28286
28388
|
if (wanted.length > 0 || (await recordedConnectionIds(resolved)).length > 0)
|
|
28287
28389
|
steps.push(connectionsStep(resolved, projectId, client, wanted));
|
|
@@ -28317,8 +28419,11 @@ async function deployIdentity(resolved, projectId, deps, options = {}) {
|
|
|
28317
28419
|
const brandingRan = outcomes.some(
|
|
28318
28420
|
(outcome) => outcome.step === IDENTITY_BRANDING_STEP && !outcome.skipped
|
|
28319
28421
|
);
|
|
28422
|
+
const portalAddressRan = outcomes.some(
|
|
28423
|
+
(outcome) => outcome.step === IDENTITY_PORTAL_ADDRESS_STEP && !outcome.skipped
|
|
28424
|
+
);
|
|
28320
28425
|
return {
|
|
28321
|
-
status: ran || connectionsRan || brandingRan ? "deployed" : "unchanged",
|
|
28426
|
+
status: ran || connectionsRan || brandingRan || portalAddressRan ? "deployed" : "unchanged",
|
|
28322
28427
|
steps: outcomes.map((outcome) => ({
|
|
28323
28428
|
step: outcome.step,
|
|
28324
28429
|
skipped: outcome.skipped
|
|
@@ -29753,7 +29858,7 @@ var deploy_default = {
|
|
|
29753
29858
|
}),
|
|
29754
29859
|
handler: async (argv) => {
|
|
29755
29860
|
await captureEvent({ argv, event: "apitogo deploy" });
|
|
29756
|
-
if (await
|
|
29861
|
+
if (!await isLegacyConfig(path85.resolve(process.cwd(), argv.dir))) {
|
|
29757
29862
|
await deployProject(argv);
|
|
29758
29863
|
return;
|
|
29759
29864
|
}
|
|
@@ -31579,7 +31684,7 @@ var teardown_default = {
|
|
|
31579
31684
|
}),
|
|
31580
31685
|
handler: async (argv) => {
|
|
31581
31686
|
await captureEvent({ argv, event: "apitogo teardown" });
|
|
31582
|
-
if (await
|
|
31687
|
+
if (!await isLegacyConfig(path95.resolve(process.cwd(), argv.dir))) {
|
|
31583
31688
|
await teardownProject(argv);
|
|
31584
31689
|
return;
|
|
31585
31690
|
}
|
package/dist/cli/worker.js
CHANGED
|
@@ -113,6 +113,9 @@ var ProtectedRoutesSchema = z.pipe(
|
|
|
113
113
|
z.transform(normalizeProtectedRoutes)
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
+
// src/vite/prerender/utils.ts
|
|
117
|
+
var prerenderFileFor = (urlPath) => urlPath === "/" ? "/index.html" : urlPath.endsWith("/index") ? `${urlPath}/index.html` : `${urlPath}.html`;
|
|
118
|
+
|
|
116
119
|
// src/vite/prerender/worker.ts
|
|
117
120
|
var { template, distDir, serverConfigPath, entryServerPath, writeRedirects } = Piscina.workerData;
|
|
118
121
|
var isStaticNavigateWarning = (args) => typeof args[0] === "string" && args[0].includes("<Navigate> must not be used on the initial render");
|
|
@@ -128,7 +131,7 @@ var config = await runPluginTransformConfig(rawConfig);
|
|
|
128
131
|
var routes = server.getRoutesByConfig(config);
|
|
129
132
|
var { basePath } = config;
|
|
130
133
|
var renderPage = async ({ urlPath }) => {
|
|
131
|
-
const filename = urlPath
|
|
134
|
+
const filename = prerenderFileFor(urlPath);
|
|
132
135
|
const pathname = joinUrl(basePath, urlPath);
|
|
133
136
|
const url = joinUrl("http://localhost", pathname);
|
|
134
137
|
const outputPath = path.join(distDir, filename);
|
package/docs/cli/overview.md
CHANGED
|
@@ -70,16 +70,15 @@ Secrets are project secrets (`npx apitogo secrets set`), never files. Details:
|
|
|
70
70
|
|
|
71
71
|
## CLI vs MCP
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
| Status / teardown | `npx apitogo status` / `npx apitogo teardown` | `verify_dev_portal_live` / `teardown_project` |
|
|
73
|
+
Each MCP tool runs one CLI command in the project folder, so both give the same result.
|
|
74
|
+
|
|
75
|
+
| Task | CLI | MCP (agent) |
|
|
76
|
+
| ----------------- | --------------------------------------------- | ------------------------------------- |
|
|
77
|
+
| Create | `npx @lukoweb/apitogo@latest create` | `apitogo_create` |
|
|
78
|
+
| Shape config | `npx apitogo config` + file edits | `apitogo_config` + file edits |
|
|
79
|
+
| Preview | `npx apitogo run` | `npx apitogo run` (you start it) |
|
|
80
|
+
| Deploy | `npx apitogo plan` / `npx apitogo deploy` | `apitogo_plan` / `apitogo_deploy` |
|
|
81
|
+
| Status / teardown | `npx apitogo status` / `npx apitogo teardown` | `apitogo_status` / `apitogo_teardown` |
|
|
83
82
|
|
|
84
83
|
Prefer MCP when an agent should drive changes. Prefer CLI for CI, scripts, or terminal-only
|
|
85
84
|
workflows.
|
package/docs/cli/setup.md
CHANGED
|
@@ -6,8 +6,8 @@ sidebar_icon: terminal
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Use the **`apitogo`** CLI (`@lukoweb/apitogo`) to scaffold a local portal, shape
|
|
9
|
-
`apitogo.config.json`, preview with `npx apitogo run`, and publish with `npx apitogo deploy`.
|
|
10
|
-
|
|
9
|
+
`apitogo.config.json`, preview with `npx apitogo run`, and publish with `npx apitogo deploy`. The
|
|
10
|
+
[MCP tools](../mcp/tools.md) run these same commands for an agent.
|
|
11
11
|
|
|
12
12
|
Examples in these docs use **`npx apitogo`** so they run the project-local CLI. A global install is
|
|
13
13
|
optional and not required.
|
|
@@ -79,7 +79,8 @@ An image in the portal's `public/` folder, or an `https://` address:
|
|
|
79
79
|
"logo": "apitogo/portal/public/logo.svg"
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
One image per color scheme, with the text read out by screen readers
|
|
82
|
+
One image per color scheme, with the text read out by screen readers. `dark` and `alt` are optional;
|
|
83
|
+
without `dark` the light image is shown in both:
|
|
83
84
|
|
|
84
85
|
```json
|
|
85
86
|
"logo": {
|
package/docs/mcp/go-live.md
CHANGED
|
@@ -1,100 +1,72 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Go live with MCP
|
|
3
3
|
description:
|
|
4
|
-
|
|
4
|
+
Create the platform project, preview the deploy, deploy and follow the result with the MCP tools.
|
|
5
5
|
sidebar_icon: rocket
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Going live puts the project on the APItoGo platform: the portal gets its address, the gateway starts
|
|
9
|
+
protecting the API, and sign-in and plans work. It is the same as `apitogo deploy` in a terminal.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 1. Make the platform project
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
invent Free + Pro.
|
|
15
|
-
2. Save secrets in the site **`.env`** (never in chat):
|
|
13
|
+
> Put Books on APItoGo.
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
APITOGO_CONFIG_AUTHENTICATION__OIDC__CLIENT_ID=your-client-id
|
|
20
|
-
APITOGO_CONFIG_AUTHENTICATION__OIDC__CLIENT_SECRET=your-client-secret
|
|
21
|
-
# Only when you have paid project plans:
|
|
22
|
-
APITOGO_CONFIG_AUTHENTICATION__STRIPE__SECRET_KEY=sk_test_...
|
|
23
|
-
APITOGO_CONFIG_AUTHENTICATION__STRIPE__WEBHOOK_SECRET=whsec_...
|
|
24
|
-
```
|
|
15
|
+
The agent calls **`apitogo_project_create`**. It creates the project and writes its id into
|
|
16
|
+
`apitogo.config.json`.
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
| Plan | When |
|
|
19
|
+
| ------------------------- | ----------------------------------------------------- |
|
|
20
|
+
| `free` | The default; enough to try everything without payment |
|
|
21
|
+
| `starter`, `professional` | Needed to sell paid API plans |
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
A paid plan answers with a checkout link: you open it and pay; the agent never pays. Already have a
|
|
24
|
+
project? **`apitogo_project_use`** points the folder at it by its id.
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
apply_product_intent({ mode: "goLive", siteDirectory, platformPlan?, ... })
|
|
34
|
-
→ check_dev_portal_publish_readiness
|
|
35
|
-
→ ensure_platform_plan (if needed)
|
|
36
|
-
→ publish_dev_portal
|
|
37
|
-
→ verify_dev_portal_live
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
If status is `blocked`, fix the readiness checklist (missing secrets, plan, OpenAPI). Do not invent
|
|
41
|
-
credentials.
|
|
42
|
-
|
|
43
|
-
After success, register **`manualSteps.oidcRedirectUri`** in your IdP. When `stripeRequired` is
|
|
44
|
-
true, create a Stripe webhook at `manualSteps.stripeWebhookUrl` for the listed events.
|
|
45
|
-
|
|
46
|
-
Do not treat warm-pool URLs as live until publish / deploy / verify succeed.
|
|
26
|
+
## 2. Secrets
|
|
47
27
|
|
|
48
|
-
|
|
28
|
+
Paid plans need the project's Stripe key and webhook secret. Set them yourself in a terminal; a
|
|
29
|
+
secret's value never goes through the agent:
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Docs and billing before the API backend:
|
|
53
|
-
|
|
54
|
-
```text
|
|
55
|
-
shape → publish_dev_portal → deploy_backend → sync_gateway_plans_from_manifest(syncActionsOnly: true)
|
|
31
|
+
```bash
|
|
32
|
+
npx apitogo secrets set STRIPE_API_KEY
|
|
56
33
|
```
|
|
57
34
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Backend already healthy:
|
|
61
|
-
|
|
62
|
-
```text
|
|
63
|
-
shape → deploy_backend → publish_dev_portal(skipBackendRedeploy: true)
|
|
35
|
+
```bash
|
|
36
|
+
npx apitogo secrets set STRIPE_WEBHOOK
|
|
64
37
|
```
|
|
65
38
|
|
|
66
|
-
|
|
39
|
+
**`apitogo_secrets_list`** shows which names are set. See the
|
|
40
|
+
[Stripe guide](../guides/stripe-setup.mdx).
|
|
67
41
|
|
|
68
|
-
|
|
42
|
+
## 3. Preview, then deploy
|
|
69
43
|
|
|
70
|
-
|
|
44
|
+
- **`apitogo_plan`** says, for each service, whether a deploy would change it and why. It changes
|
|
45
|
+
nothing.
|
|
46
|
+
- **`apitogo_deploy`** deploys every service that changed: identity, gateway, plans, then the
|
|
47
|
+
portal. The first deploy takes several minutes; later ones skip what did not change. `only` limits
|
|
48
|
+
it to some services, `force` redeploys them anyway.
|
|
71
49
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
| Platform hosting | Free / Starter / Professional on APItoGo | `ensure_platform_plan` |
|
|
75
|
-
| Gateway / API | End-user SKUs and rate limits on `/pricing` | Manifest / intent `plans` |
|
|
50
|
+
A change that cannot be undone, such as switching the identity provider, stops the deploy with an
|
|
51
|
+
explanation. The agent asks you, and only then deploys again with `confirmIrreversible`.
|
|
76
52
|
|
|
77
|
-
##
|
|
53
|
+
## 4. See what is live
|
|
78
54
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`sync_gateway_plans_from_manifest` before publish.
|
|
82
|
-
- After a post-publish `deploy_backend`, sync actions with
|
|
83
|
-
`sync_gateway_plans_from_manifest({ syncActionsOnly: true })`.
|
|
84
|
-
- Resume from `.apitogo/publish-state.json` or `resumeFromStep` if a previous publish stopped early.
|
|
55
|
+
**`apitogo_status`** shows each service's state and its addresses: the portal, the gateway's base
|
|
56
|
+
URL and the sign-in issuer.
|
|
85
57
|
|
|
86
|
-
##
|
|
58
|
+
## When something fails
|
|
87
59
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
| MCP `publish_dev_portal` | Full product go-live (plans, auth, hosting, verify) |
|
|
91
|
-
| CLI `npx apitogo deploy` | Publish config to platform (apihub + portal) |
|
|
60
|
+
Every tool answers with the CLI's JSON. A failure carries `error` and usually a `hint` with the next
|
|
61
|
+
step, for example to log in:
|
|
92
62
|
|
|
93
|
-
|
|
63
|
+
| Message | What to do |
|
|
64
|
+
| ------------------------ | --------------------------------------------------------------------------------- |
|
|
65
|
+
| Not logged in | Run `npx apitogo login`, or give the server API credentials ([setup](./setup.md)) |
|
|
66
|
+
| A secret is missing | Set it with `npx apitogo secrets set NAME` |
|
|
67
|
+
| Hosting is not ready yet | Wait a few minutes, then deploy again |
|
|
94
68
|
|
|
95
|
-
##
|
|
69
|
+
## Remove it
|
|
96
70
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- [Authentication](../configuration/authentication.md)
|
|
100
|
-
- [Manifest](../configuration/overview.md)
|
|
71
|
+
**`apitogo_teardown`** deletes the platform project (local files stay), or with `only` just its
|
|
72
|
+
gateway, identity or plans. The agent must ask you and type the project's name back.
|
package/docs/mcp/overview.md
CHANGED
|
@@ -1,76 +1,58 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: MCP overview
|
|
3
3
|
description:
|
|
4
|
-
|
|
4
|
+
Let an AI agent create, shape, check and deploy an APItoGo project through the APItoGo MCP server.
|
|
5
5
|
sidebar_icon: bot
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
The **APItoGo MCP** server
|
|
9
|
-
|
|
8
|
+
The **APItoGo MCP** server (`@lukoweb/apitogo-mcp`) gives an AI agent the same commands you run in a
|
|
9
|
+
terminal: create a project, add services, change plans and pages, check, preview and deploy. Each
|
|
10
|
+
tool runs the [`apitogo` CLI](../cli/overview.md) in the project folder, so an agent and a person
|
|
11
|
+
always get the same result.
|
|
10
12
|
|
|
11
|
-
**First time?**
|
|
12
|
-
**[Configure MCP in your agent](./setup.md)**. Then follow the [Quickstart](../quickstart.md).
|
|
13
|
+
**First time?** Wire the server into your agent → **[Configure MCP in your agent](./setup.md)**.
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## How it fits together
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
```
|
|
18
|
+
agent ──MCP tool──> apitogo-mcp ──runs──> apitogo <command> --json (in the project folder)
|
|
19
|
+
│
|
|
20
|
+
├── apitogo.config.json, apitogo/ (your files)
|
|
21
|
+
└── APItoGo platform (deploy, status)
|
|
22
|
+
```
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
- Everything the project is lives in `apitogo.config.json` (config version 2) and the `apitogo/`
|
|
25
|
+
folder. See [Configuration](../configuration/overview.md).
|
|
26
|
+
- The tools answer with the CLI's JSON, so the agent sees what changed, what a deploy would do, and
|
|
27
|
+
why something failed.
|
|
28
|
+
- The agent can also edit the project's files directly (the landing page, docs pages, OpenAPI file,
|
|
29
|
+
`plans.json`) when no tool covers a change.
|
|
24
30
|
|
|
25
|
-
##
|
|
31
|
+
## The usual flow
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
| Step | Tool |
|
|
34
|
+
| ------------------------- | ---------------------------------------------------------- |
|
|
35
|
+
| Make the project folder | `apitogo_create`, or `apitogo_init` + `apitogo_add` |
|
|
36
|
+
| Shape it | `apitogo_config` (name, OpenAPI, pages, plans), file edits |
|
|
37
|
+
| Check it | `apitogo_check` |
|
|
38
|
+
| Make the platform project | `apitogo_project_create` |
|
|
39
|
+
| Preview the deploy | `apitogo_plan` |
|
|
40
|
+
| Deploy | `apitogo_deploy` |
|
|
41
|
+
| See what is live | `apitogo_status` |
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
| -------------------- | -------- | ----------------- |
|
|
31
|
-
| `APITOGO_API_KEY_ID` | Yes | Platform API auth |
|
|
32
|
-
| `APITOGO_API_SECRET` | Yes | Platform API auth |
|
|
43
|
+
Details: [Shape a project](./shape.md) · [Go live](./go-live.md) · [All tools](./tools.md).
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
## What the agent never does
|
|
35
46
|
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
You choose how to edit the portal:
|
|
44
|
-
|
|
45
|
-
| Approach | When it helps |
|
|
46
|
-
| ----------------------- | --------------------------------------------------------------------------------------------------- |
|
|
47
|
-
| **MCP tools** | Agent-driven changes; tools can wire navigation/config and run validation. See [Tools](./tools.md). |
|
|
48
|
-
| **Edit files directly** | Full control in your editor — `apitogo.config.json`, `pages/**`, `landing/LandingPage.tsx`, etc. |
|
|
49
|
-
|
|
50
|
-
Useful MCP tools (optional — not required):
|
|
51
|
-
|
|
52
|
-
| Change | Tool |
|
|
53
|
-
| ----------------------------------- | ------------------------------------------------ |
|
|
54
|
-
| Add / edit a docs page | `add_dev_portal_page` / `update_dev_portal_page` |
|
|
55
|
-
| OpenAPI | `set_dev_portal_openapi` |
|
|
56
|
-
| Plans, branding, backend, projectId | `update_dev_portal_manifest` |
|
|
57
|
-
| Theme | `set_dev_portal_theme` |
|
|
58
|
-
|
|
59
|
-
Home page content often lives in `landing/LandingPage.tsx`. Site footer can live under
|
|
60
|
-
`modules.landing.content.footer` in `apitogo.config.json`.
|
|
61
|
-
|
|
62
|
-
## Config model (local site)
|
|
63
|
-
|
|
64
|
-
| File | Role |
|
|
65
|
-
| --------------------- | ------------------------------------- |
|
|
66
|
-
| `apitogo.config.json` | Site, modules, plans, auth type, apis |
|
|
67
|
-
| `.env` | `VITE_*` + `APITOGO_CONFIG_*` secrets |
|
|
68
|
-
|
|
69
|
-
Details: [Manifest](../configuration/overview.md).
|
|
47
|
+
- **Handle secret values.** You set secrets such as the Stripe key yourself with
|
|
48
|
+
`apitogo secrets set NAME` in a terminal. The agent can list their names.
|
|
49
|
+
- **Delete without you.** `apitogo_teardown` needs the project's name typed back, and the server
|
|
50
|
+
tells the agent to ask you first, as for removing a secret, choosing a paid plan, or a deploy
|
|
51
|
+
change that cannot be undone.
|
|
52
|
+
- **Pay.** A paid project plan answers with a checkout link that you open and pay.
|
|
70
53
|
|
|
71
54
|
## Next
|
|
72
55
|
|
|
73
56
|
- [Configure MCP](./setup.md)
|
|
74
|
-
- [
|
|
75
|
-
-
|
|
76
|
-
- CLI without an agent: [CLI overview](../cli/overview.md)
|
|
57
|
+
- [Shape a project](./shape.md) · [Go live](./go-live.md) · [Tools](./tools.md)
|
|
58
|
+
- Without an agent: [CLI overview](../cli/overview.md)
|