@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 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
- /** The logo in the portal header: one image, or one per color scheme (API-129). */
5935
- logo: z.union([
5936
- path3,
5937
- z.strictObject({ light: path3, dark: path3, alt: z.string().optional() })
5938
- ]).optional(),
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: logoSource(configDir, portal.logo.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.57",
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 === "/" ? "/index.html" : `${r.path}.html`
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 isConfigV2(path85.resolve(process.cwd(), argv.dir))) {
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 isConfigV2(path95.resolve(process.cwd(), argv.dir))) {
31687
+ if (!await isLegacyConfig(path95.resolve(process.cwd(), argv.dir))) {
31583
31688
  await teardownProject(argv);
31584
31689
  return;
31585
31690
  }
@@ -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 === "/" ? "/index.html" : `${urlPath}.html`;
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);
@@ -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
- Both paths use the same config-first model. `npx apitogo deploy` runs the same engine as MCP
74
- `publish_dev_portal`.
75
-
76
- | Task | CLI | MCP (agent) |
77
- | ----------------- | --------------------------------------------- | --------------------------------------------- |
78
- | Scaffold | `npx @lukoweb/apitogo@latest init` | `create_dev_portal` |
79
- | Shape config | `npx apitogo config` + file edits | `apply_product_intent` (shape) |
80
- | Preview | `npx apitogo run` | `npm run dev` / preview tool |
81
- | Publish | `npx apitogo deploy` | `publish_dev_portal` |
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
- Platform commands (`deploy`, `status`, `teardown`) use the same engine as MCP `publish_dev_portal`.
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": {
@@ -1,100 +1,72 @@
1
1
  ---
2
2
  title: Go live with MCP
3
3
  description:
4
- Publish an APIToGo developer portal — platform plans, secrets, publish_dev_portal, and verify.
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
- **Go live** publishes your shaped portal to APItoGo hosting, optionally deploys the backend, upserts
9
- gateway plans, and verifies the live site.
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
- ## Before you publish
11
+ ## 1. Make the platform project
12
12
 
13
- 1. Confirm **gateway / API plans** in the manifest (or pass them in `apply_product_intent`). Do not
14
- invent Free + Pro.
15
- 2. Save secrets in the site **`.env`** (never in chat):
13
+ > Put Books on APItoGo.
16
14
 
17
- ```sh
18
- APITOGO_CONFIG_AUTHENTICATION__OIDC__AUTHORITY=https://login.example.com
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
- 3. Choose a **platform hosting** plan: **Free**, **Starter**, or **Professional**. Agents should
27
- call `list_platform_plans` / explain `freeProjectsQuota` before you pick. Starter and
28
- Professional use Stripe checkout (`checkoutUrl`).
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
- ## Happy path
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
- ```text
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
- ## Granular go-live paths
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
- ### Portal-first
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
- ### Full-stack
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
- ### CI one-shot
39
+ **`apitogo_secrets_list`** shows which names are set. See the
40
+ [Stripe guide](../guides/stripe-setup.mdx).
67
41
 
68
- `bootstrap_dev_portal` provisions and deploys in one call (advanced).
42
+ ## 3. Preview, then deploy
69
43
 
70
- ## Platform plan vs API plans
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
- | Kind | Meaning | How |
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
- ## Publish behavior
53
+ ## 4. See what is live
78
54
 
79
- - Unchanged healthy backend/site artifacts are reused unless you force redeploy.
80
- - `publish_dev_portal` upserts gateway plans from the manifest; do **not** call
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
- ## CLI deploy vs MCP publish
58
+ ## When something fails
87
59
 
88
- | Path | Use when |
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
- See [Go live with CLI](../cli/go-live.md).
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
- ## Related
69
+ ## Remove it
96
70
 
97
- - [Shape](./shape.md)
98
- - [Tools](./tools.md)
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.
@@ -1,76 +1,58 @@
1
1
  ---
2
2
  title: MCP overview
3
3
  description:
4
- Use the APItoGo MCP server to scaffold, shape, and publish API products from an AI agent.
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 lets AI agents manage projects, developer portals, gateway plans, backend
9
- deploy, and go-live. You can use MCP tools, edit site files yourself, or mix both.
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?** Install and wire the server in your agent →
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
- ## What you get
15
+ ## How it fits together
15
16
 
16
- - Scaffold a local portal site (`create_dev_portal` / `apply_product_intent`)
17
- - Shape OpenAPI, plans, branding, theme, and docs pages
18
- - Publish to APItoGo hosting (`publish_dev_portal`) with platform plans **Free**, **Starter**, or
19
- **Professional**
20
- - Deploy backends and sync gateway actions after publish
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
- Prefer **`apply_product_intent`** for “create / shape / go live” prompts. See [Shape](./shape.md)
23
- and [Go live](./go-live.md).
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
- ## Prerequisites
31
+ ## The usual flow
26
32
 
27
- ### MCP host environment
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
- | Variable | Required | Purpose |
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
- ### Agent host
45
+ ## What the agent never does
35
46
 
36
- - Node.js 20+ (scaffold, build, preview)
37
- - Network access to the npm registry during install/build
38
- - OIDC credentials at publish time (site `.env` only — never in chat)
39
- - Stripe secrets at publish only when you have **paid** project plans
40
-
41
- ## Changing your site
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
- - [Quickstart](../quickstart.md)
75
- - [Shape](./shape.md) · [Go live](./go-live.md) · [Tools](./tools.md)
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)