@koda-sl/baker-cli 0.158.1 → 0.159.0

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/README.md CHANGED
@@ -2642,7 +2642,7 @@ baker mcp add --name mine --url https://my.dev/mcp --scope user
2642
2642
  baker mcp remove --name weather
2643
2643
  ```
2644
2644
 
2645
- - `--url` must be HTTPS and public; loopback / private / cloud-metadata hosts are rejected.
2645
+ - `--url` must be the **full MCP endpoint** (commonly ending in `/mcp`), HTTPS and public; loopback / private / cloud-metadata hosts are rejected. A bare domain frequently accepts the OAuth sign-in and then serves no tools, so every address is handshake-checked after it changes: `connected` and `list` report `usable: false` plus a `problem` (and a `suggestedAddress` when the server advertises a better endpoint) for a server that isn't serving tools, or `lastCheck: failed` when the last check only hit an outage — that one may work anyway, and a later turn re-checks it automatically. Repointing a server at a different host clears its saved sign-in: a token belongs to the host that issued it, so the server has to be connected again.
2646
2646
  - Scope is two independent choices — audience (just you / everyone) × level (this company / whole org):
2647
2647
  - `--scope company` (default) — everyone in this company.
2648
2648
  - `--scope org` — everyone across the org; needs an admin API key.
@@ -4809,6 +4809,7 @@ baker brand fonts fetch "Inter" --subsets latin,latin-ext # add accented-glyph
4809
4809
  - **Failures carry the numbers, so nothing needs a second call.** `check`'s error keeps the full per-family breakdown at `error.fix.families` (healthy families included, so a multi-family brand shows what not to touch), and both commands probe the standard weights to report the ones that *do* exist at `error.fix.served` — the remediation is "retry with one of these" rather than "run `check`", which would be the command that just failed.
4810
4810
  - **`fetch`** downloads the family into `src/brand/fonts/` and prints the `@font-face` block to paste above `@theme`. Defaults to the `latin` subset; `--subsets latin,latin-ext` adds accented-glyph coverage for Spanish and Portuguese copy. One file per weight *and* subset, since Google ships them separately and `unicode-range` is carried through verbatim.
4811
4811
  - **It does not edit `global.css` for you.** Paste the block, then drop that family from the Google `@import` — otherwise the page self-hosts *and* still calls Google. Record the weights in `src/brand/BRAND.md` in the same change.
4812
+ - **The emitted `src:` is relative to `global.css` (`../brand/fonts/…`) on purpose — keep it that way.** A root-relative `/src/brand/fonts/…` reads to the bundler as a path something else already serves, so the file never lands in the published build and every page 404s it. That failure is silent: an unfetchable face falls back to a system font, so the site just renders off-brand. Kept relative, the bundler resolves it on disk, content-hashes it, and ships it. The scaffold's `validate-public-asset-refs` blocks the root-relative form.
4812
4813
  - **Licensing:** Google Fonts are open-licensed and safe to self-host. A client's own commercial typeface is not — confirm before re-hosting one.
4813
4814
 
4814
4815
  Complements the offline `validate-brand-fonts` check that runs in the scaffold's `verify`: that one compares what `global.css` declares against what provides it, with no network. Only the network can answer whether Google really serves a given family and weight.
package/dist/cli.js CHANGED
@@ -16618,7 +16618,10 @@ function fontFileName(face) {
16618
16618
  const style = face.style === "italic" ? "italic" : "";
16619
16619
  return `${family}-${face.weight}${style}-${face.subset}.woff2`;
16620
16620
  }
16621
- function renderFontFaceCss(faces, hrefFor) {
16621
+ function fontFaceHref(face) {
16622
+ return `../brand/fonts/${fontFileName(face)}`;
16623
+ }
16624
+ function renderFontFaceCss(faces) {
16622
16625
  return faces.map(
16623
16626
  (face) => [
16624
16627
  "@font-face {",
@@ -16626,7 +16629,7 @@ function renderFontFaceCss(faces, hrefFor) {
16626
16629
  ` font-style: ${face.style};`,
16627
16630
  ` font-weight: ${face.weight};`,
16628
16631
  " font-display: swap;",
16629
- ` src: url('${hrefFor(face)}') format('woff2');`,
16632
+ ` src: url('${fontFaceHref(face)}') format('woff2');`,
16630
16633
  ...face.unicodeRange ? [` unicode-range: ${face.unicodeRange};`] : [],
16631
16634
  "}"
16632
16635
  ].join("\n")
@@ -16866,7 +16869,7 @@ var fontsFetchCommand = defineCommand86({
16866
16869
  explanation: "The font stylesheet resolved but the font files themselves did not download."
16867
16870
  });
16868
16871
  }
16869
- const fontFace = renderFontFaceCss(downloaded, (f) => `/${FONTS_DIR}/${fontFileName(f)}`);
16872
+ const fontFace = renderFontFaceCss(downloaded);
16870
16873
  const downloadedWeights = [...new Set(downloaded.map((f) => f.weight))].sort((a, b) => a - b);
16871
16874
  const incomplete = weights.filter((w) => !downloadedWeights.includes(w));
16872
16875
  writeJson({
@@ -29491,6 +29494,20 @@ function parseScope(raw) {
29491
29494
  function actingUserId() {
29492
29495
  return getEnv().BAKER_ACTING_USER_ID;
29493
29496
  }
29497
+ function customEntry(server) {
29498
+ const failed = server.health && !server.health.ok;
29499
+ const problem = server.health?.message ?? "This server's address is not serving tools.";
29500
+ return {
29501
+ name: server.name,
29502
+ scope: server.scope,
29503
+ enabled: server.enabled,
29504
+ ...failed && !server.health?.transient ? { usable: false, problem } : {},
29505
+ ...failed && server.health?.transient ? { lastCheck: "failed", problem } : {},
29506
+ ...failed && server.health?.suggestedUrl ? { suggestedAddress: server.health.suggestedUrl } : {}
29507
+ };
29508
+ }
29509
+ var BROKEN_SERVER_HINT = "A server marked usable=false will NOT answer: its tools are absent from this turn. Say plainly that the tool is not working and relay `problem`; never claim its tools are loading, coming next message, or temporarily unavailable. Fixing it needs a person: the user updates its address in the dashboard (Brain \u2192 Integrations \u2192 MCPs). If `suggestedAddress` is present, that is the address to use.";
29510
+ var LAST_CHECK_FAILED_HINT = "A server with lastCheck=failed was unreachable or erroring when it was last checked \u2014 that may already have passed, and its tools ARE loaded this turn. Try the tool; if it works, say nothing about the check. If it fails, report `problem` as what happened rather than guessing.";
29494
29511
  function parseHeaders(raw) {
29495
29512
  const list = raw === void 0 ? [] : Array.isArray(raw) ? raw : [raw];
29496
29513
  const headers = {};
@@ -29537,8 +29554,14 @@ A tool the user names that is NOT listed here is simply not connected yet.`
29537
29554
  ...c.accountLabel ? { account: c.accountLabel } : {},
29538
29555
  ...c.readOnly ? { readOnly: true } : {}
29539
29556
  }));
29540
- const custom = data.custom.map((s) => ({ name: s.name, scope: s.scope, enabled: s.enabled }));
29557
+ const custom = data.custom.map(customEntry);
29541
29558
  const hints = [];
29559
+ if (data.custom.some((s) => s.health && !s.health.ok && !s.health.transient)) {
29560
+ hints.push(BROKEN_SERVER_HINT);
29561
+ }
29562
+ if (data.custom.some((s) => s.health && !s.health.ok && s.health.transient)) {
29563
+ hints.push(LAST_CHECK_FAILED_HINT);
29564
+ }
29542
29565
  if (data.managedDisabled) {
29543
29566
  hints.push("Managed integrations are disabled for this company \u2014 only custom MCP servers load.");
29544
29567
  }
@@ -29574,7 +29597,15 @@ var listCommand13 = defineCommand139({
29574
29597
  "/api/mcp/custom",
29575
29598
  user ? { actingUserId: user } : void 0
29576
29599
  );
29577
- writeJson({ ok: true, data: data.servers });
29600
+ writeJson({
29601
+ ok: true,
29602
+ // Detail view: the full row (address included) plus the usability verdict.
29603
+ // `health` itself is folded into `usable`/`problem` by `customEntry`.
29604
+ data: data.servers.map((server) => ({ ...server, health: void 0, ...customEntry(server) })),
29605
+ ...data.servers.some((s) => s.health && !s.health.ok) ? {
29606
+ hints: data.servers.some((s) => s.health && !s.health.ok && !s.health.transient) ? [BROKEN_SERVER_HINT] : [LAST_CHECK_FAILED_HINT]
29607
+ } : {}
29608
+ });
29578
29609
  } catch (err) {
29579
29610
  fail6(err);
29580
29611
  }
@@ -29585,7 +29616,11 @@ registerSchema({
29585
29616
  description: "Register a custom MCP server. Tools appear as mcp__<name>__* on the next message. URL must be HTTPS. Scope: company (default, all chats) | org (org admin key) | user (only the current sender).",
29586
29617
  args: {
29587
29618
  name: { type: "string", description: "Server name \u2192 tools appear as mcp__<name>__*", required: true },
29588
- url: { type: "string", description: "HTTPS MCP endpoint", required: true },
29619
+ url: {
29620
+ type: "string",
29621
+ description: "Full HTTPS MCP endpoint (commonly ends in /mcp, not just the domain)",
29622
+ required: true
29623
+ },
29589
29624
  scope: { type: "string", description: "user | user_org | company | org (default company)", required: false },
29590
29625
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
29591
29626
  }
@@ -29604,7 +29639,11 @@ Examples:
29604
29639
  },
29605
29640
  args: {
29606
29641
  name: { type: "string", description: "Server name (mcp__<name>__*)", required: true },
29607
- url: { type: "string", description: "HTTPS MCP endpoint", required: true },
29642
+ url: {
29643
+ type: "string",
29644
+ description: "Full HTTPS MCP endpoint (commonly ends in /mcp, not just the domain)",
29645
+ required: true
29646
+ },
29608
29647
  scope: { type: "string", description: "user | user_org | company | org (default company)", required: false },
29609
29648
  header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
29610
29649
  },
@@ -29620,7 +29659,14 @@ Examples:
29620
29659
  ...user ? { actingUserId: user } : {},
29621
29660
  ...headers ? { headers } : {}
29622
29661
  });
29623
- writeJson({ ok: true, data });
29662
+ writeJson({
29663
+ ok: true,
29664
+ data,
29665
+ hints: [
29666
+ "The address must be the full MCP endpoint (commonly ending in /mcp), not just the domain \u2014 a domain on its own usually answers sign-in and then serves no tools.",
29667
+ "The address is checked in the background. Run `baker mcp connected` on the next message to confirm it serves tools before promising the user anything."
29668
+ ]
29669
+ });
29624
29670
  } catch (err) {
29625
29671
  fail6(err);
29626
29672
  }