diff --git a/.sync/dep-parity.json b/.sync/dep-parity.json index 4ddc57530..40fe3a8fc 100644 --- a/.sync/dep-parity.json +++ b/.sync/dep-parity.json @@ -1,6 +1,6 @@ { "$note": "Upstream's pinned versions for every dependency both trees declare in the SAME section, snapshotted at `cursor`. The sync ports deltas, which is correct per commit and lets a one-time divergence become permanent: once a version is off upstream's line, every later `chore(deps)` batch skips it, because those ports bump only where this fork already matched upstream's pre-image. `prettier` sat at ^3.8.4 against upstream's ^3.9.6 for that reason, through four ported batches, until this file was written. Section-aware on purpose: 18 packages are declared on both sides but in different sections — the whole `@tiptap/*` family is a peer `^3` upstream and a dependency `^3.29.2` here, and `ai` is a peer there and a devDependency here. Those are structural divergences, not drift, and comparing a peer range against a dependency range says nothing. They are absent from this file by construction rather than by omission. Guarded by `test/utils/dep-parity.spec.ts`. Refresh with `node .sync/dep-parity.mjs [cursor]`, which preserves `exceptions`.", - "cursor": "bb55709fee1da7752895e27f44384d3c5c130425", + "cursor": "9ef3ee394339ea477e34e122f22f0c5b039ee37d", "manifests": { "package.json": { "dependencies": { diff --git a/.sync/log/9ef3ee394339ea477e34e122f22f0c5b039ee37d.md b/.sync/log/9ef3ee394339ea477e34e122f22f0c5b039ee37d.md new file mode 100644 index 000000000..697571ab5 --- /dev/null +++ b/.sync/log/9ef3ee394339ea477e34e122f22f0c5b039ee37d.md @@ -0,0 +1,84 @@ +# Skip: docs: improve agent readiness + +**Upstream:** `9ef3ee394339ea477e34e122f22f0c5b039ee37d` (nuxt/ui, #6878) +**Decision:** skip by maintainer decision — but it exposed a fork defect, fixed here + +## Upstream change + +19 files, +1387/−139: docs-site infrastructure for machine readers. Content +negotiation so `/docs/**` can return markdown to an `Accept: text/markdown` +client (`markdownNegotiation.ts`, 318 lines), a generated OpenAPI document +(`openapi.ts`, 668 lines) served at `/openapi.json`, a nitro error handler, +a markdown middleware, and refinements to `.well-known/api-catalog`, +`.well-known/mcp/server-card.json`, the `raw/` routes, sitemaps and llms.txt. + +## Why skipped + +Not applicable as a body of work: this fork's agent surface is its **own**, and +larger. `docs/server/mcp/` carries 27 files — 12 tools, 4 resources, 2 prompts — +against upstream's smaller set, wired through `@nuxtjs/mcp-toolkit` at `/mcp/`. +Porting upstream's plumbing would mean re-deciding architecture we already have. +The OpenAPI half in particular describes upstream's `/api/**` surface, which is +not ours. + +Skipped on maintainer decision, with the queue advancing past it. + +## What it did expose + +Reading the commit turned up a real inconsistency **of our own**, unrelated to +whether the port lands. + +`docs/nuxt.config.ts` advertises six discovery endpoints from `/` in an RFC 8288 +`Link` header. Four are prerendered and present in the static build. **Two never +existed at all:** + +``` +; rel="api-catalog" +; rel="service-desc" +``` + +Nothing served them — no route under `docs/server/routes/`, nothing in +`.output/public/.well-known/` but `skills`. `raw/index.md.get.ts:24` names the +server-card URL in a comment as though it existed. The headers were evidently +adopted without the routes behind them. + +Severity, stated honestly: the site is published with `nuxt generate` to GitHub +Pages, where `routeRules.headers` are not emitted at all — so the advertisement +is inert in production today, and this was never a live 404. It is still a +config claiming endpoints the site does not have. + +## The fix + +Both routes now exist and are **prerendered**, so they land in the static build +alongside the four endpoints the same header already names — the fork's own +convention is "advertise what we prerender", and these two were simply the +outliers. + +- **`api-catalog.get.ts`** — an RFC 9727 linkset naming only what this site + actually serves. Upstream anchors an `openapi.json` in theirs; we have none, so + it is absent by construction rather than by oversight. `service-doc` points at + `/docs/getting-started/` because this fork has **no** MCP docs page — upstream + links `/docs/getting-started/ai/mcp`, and `nuxt.config.ts:710` already carries + that path commented out for the same reason. +- **`server-card.json.get.ts`** — reads its tool, resource and prompt lists from + the running server via `listMcpDefinitions`, so the card cannot drift from what + `docs/server/mcp/` registers. + +That listing goes through the `#nuxt-mcp-toolkit/tools.mjs` virtual module, which +**does not resolve during prerender** — the first attempt failed the build with +`Package import specifier "#nuxt-mcp-toolkit/tools.mjs" is not defined`. Since +the card has to exist on static hosting, the import is attempted and degrades: +served by a real server it carries the full catalogue, prerendered it omits those +three arrays. A card without them is still valid — a client discovers the +catalogue by calling `tools/list` on the endpoint, which is the normal MCP flow. + +## Verify + +`docs:generate` goes from 1262 to **1264** routes, and both files land with +correct absolute URLs (`serverInfo.version` resolves to `2.12.0` from +`pkg.version`). Every href the catalogue names was then resolved against the +build: all present, the single exception being `/mcp/` itself, which is a live +server route rather than a static file. All six endpoints the `Link` header +advertises now exist. + +`lint` · `typecheck` · `test` · `docs:generate` — green. diff --git a/.sync/nuxt-ui.json b/.sync/nuxt-ui.json index a02fa3960..679c18994 100644 --- a/.sync/nuxt-ui.json +++ b/.sync/nuxt-ui.json @@ -1,7 +1,7 @@ { "upstream": "nuxt/ui", "branch": "v4", - "cursor": "bb55709fee1da7752895e27f44384d3c5c130425", + "cursor": "9ef3ee394339ea477e34e122f22f0c5b039ee37d", "_cursor_note": "cursor = last upstream commit ported into b24ui. The sync is manual by decision: one commit at a time, oldest-first, each with a `.sync/log/.md` journal and an entry in `processed`. There is no dispatcher, no porter workflow and no kill-switch — `.sync/PORTING.md` is the whole procedure. `processed` is maintained per port (backfilled #68-#72 on 2026-06-09).", "processed": { "2799fa6f2b25ce3eb15e050f3ef7c57d0d9a2fdb": { @@ -1649,6 +1649,12 @@ "b24ui_sha": "95d730458ea144269a07b4250a6cacc7692ad23c", "decision": "no-op", "summary": "docs(icons): document what `clientBundle.scan` covers (nuxt/ui #6880) — NO-OP: the mechanism being documented does not exist here. Upstream adds 40 lines to both icons integration pages explaining the limits of @nuxt/icon's client bundle scanner: it reads only .vue/.jsx/.tsx/.md/.mdc/.mdx/.yml/.yaml, matches icon names only as literal strings (so a list of links in a .ts file is missed, as is anything built like `i-lucide-${name}`), how clientBundle.scan.globInclude widens that list by replacing the default, and that runtime-only names cannot be bundled and are fetched from /api/_nuxt_icon under SSR or the Iconify API otherwise. None of that machinery exists in this fork, checked rather than assumed: our icons are @bitrix24/b24icons Vue COMPONENTS, imported by name and bundled by the compiler, so there is no string-literal icon name to scan for and the failure mode the paragraph warns about cannot arise; clientBundle, globInclude and /api/_nuxt_icon appear nowhere in docs/ or src/; @nuxt/icon survives only as a commented-out block at src/module.ts:139 and as prose inside an unrelated Accordion example; and both our icons pages document @bitrix24/b24icons with automatic setup and no scanner to configure. Same call and same reason as 08e75317, which the ledger records as a port of only its non-icon half." + }, + "9ef3ee394339ea477e34e122f22f0c5b039ee37d": { + "pr": "pending-merge", + "b24ui_sha": "pending-merge", + "decision": "skip", + "summary": "docs: improve agent readiness (nuxt/ui #6878) — SKIP by maintainer decision, but it exposed a fork defect that is fixed in the same PR. Upstream adds 19 files, +1387/-139 of docs-site infrastructure for machine readers: content negotiation so /docs/** can return markdown to an Accept: text/markdown client (markdownNegotiation.ts, 318 lines), a generated OpenAPI document (openapi.ts, 668 lines) at /openapi.json, a nitro error handler, a markdown middleware, and refinements to the .well-known routes, raw/ routes, sitemaps and llms.txt. Not applicable as a body of work: this fork's agent surface is its own and larger — docs/server/mcp/ carries 27 files (12 tools, 4 resources, 2 prompts) wired through @nuxtjs/mcp-toolkit at /mcp/, and the OpenAPI half describes upstream's /api/** surface, not ours. WHAT IT EXPOSED: docs/nuxt.config.ts advertises six discovery endpoints from / in an RFC 8288 Link header; four are prerendered and present in the static build, but /.well-known/api-catalog and /.well-known/mcp/server-card.json never existed at all — no route under docs/server/routes/, nothing in .output/public/.well-known/ but skills, while raw/index.md.get.ts:24 names the server-card URL in a comment as though it existed. Stated honestly, the severity is limited: the site ships via nuxt generate to GitHub Pages where routeRules.headers are not emitted, so the advertisement is inert in production and this was never a live 404 — but the config still claimed endpoints the site does not have. THE FIX: both routes now exist and are prerendered, landing in the static build alongside the four the same header already names (the fork's convention is 'advertise what we prerender'). api-catalog is an RFC 9727 linkset naming only what this site serves — upstream anchors an openapi.json in theirs and we have none, so it is absent by construction; service-doc points at /docs/getting-started/ because this fork has no MCP docs page, which nuxt.config.ts:710 already reflects with that path commented out. server-card reads its tool/resource/prompt lists from the running server via listMcpDefinitions so the card cannot drift from what docs/server/mcp/ registers — but that listing goes through the #nuxt-mcp-toolkit/tools.mjs virtual module which does not resolve during prerender (the first attempt failed the build with 'Package import specifier not defined'), so the import is attempted and degrades: full catalogue when served by a real server, those three arrays omitted when prerendered. A card without them is still valid since a client discovers the catalogue via tools/list on the endpoint. Verified: docs:generate goes 1262 -> 1264 routes, both files land with correct absolute URLs and serverInfo.version resolving to 2.12.0, and every href the catalogue names was resolved against the build — all present except /mcp/ itself, which is a live server route rather than a static file. All six advertised endpoints now exist." } } } diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts index 30732e83f..a155bb6c3 100644 --- a/docs/nuxt.config.ts +++ b/docs/nuxt.config.ts @@ -237,7 +237,11 @@ const pagesService = [ '/api/locales.json', '/404.html', '/sitemap.xml', - '/sitemap.md' + '/sitemap.md', + // Advertised from `/` by the agent-discovery `Link` header below; prerendered + // so they exist on static hosting like the other endpoints that header names. + '/.well-known/api-catalog', + '/.well-known/mcp/server-card.json' ] const extraAllowedHosts = (process?.env.NUXT_ALLOWED_HOSTS?.split(',').map((s: string) => s.trim()).filter(Boolean)) ?? [] diff --git a/docs/server/routes/.well-known/api-catalog.get.ts b/docs/server/routes/.well-known/api-catalog.get.ts new file mode 100644 index 000000000..33a630773 --- /dev/null +++ b/docs/server/routes/.well-known/api-catalog.get.ts @@ -0,0 +1,49 @@ +/** + * RFC 9727 api-catalog: a linkset naming the machine-readable descriptions of + * this site, advertised from `/` by the `Link` header in `nuxt.config.ts`. + * + * Only endpoints this site actually serves are listed. Upstream anchors an + * `openapi.json` here too; this fork has none, so it is absent by construction + * rather than by omission. + */ +export default defineEventHandler((event) => { + const config = useRuntimeConfig() + const site = `${config.public.canonicalUrl}${config.public.baseUrl}` + + const linkset = { + linkset: [ + { + 'anchor': `${site}/mcp/`, + 'service-desc': [ + { + href: `${site}/.well-known/mcp/server-card.json`, + type: 'application/json' + } + ], + 'service-doc': [ + { + href: `${site}/docs/getting-started/`, + type: 'text/html' + } + ] + }, + { + 'anchor': `${site}/docs`, + 'service-desc': [ + { href: `${site}/llms.txt`, type: 'text/plain' }, + { href: `${site}/llms-full.txt`, type: 'text/plain' }, + { href: `${site}/sitemap.md`, type: 'text/markdown' } + ], + 'service-doc': [ + { + href: `${site}/docs`, + type: 'text/html' + } + ] + } + ] + } + + setResponseHeader(event, 'Content-Type', 'application/linkset+json; charset=utf-8') + return linkset +}) diff --git a/docs/server/routes/.well-known/mcp/server-card.json.get.ts b/docs/server/routes/.well-known/mcp/server-card.json.get.ts new file mode 100644 index 000000000..491dc9377 --- /dev/null +++ b/docs/server/routes/.well-known/mcp/server-card.json.get.ts @@ -0,0 +1,69 @@ +/** + * MCP server card, advertised from `/` by the `Link` header in `nuxt.config.ts` + * as `rel="service-desc"`. + * + * The tool, resource and prompt lists are read from the running server so the + * card cannot drift from what `docs/server/mcp/` registers — but that listing + * goes through the `#nuxt-mcp-toolkit/tools.mjs` virtual module, which does not + * resolve during prerender. This site is published statically, so the card must + * still exist there: the listing is attempted and, when it is unavailable, the + * card is emitted without those three arrays rather than failing the build. + * + * A card without them is still valid — a client discovers the catalogue by + * calling `tools/list` on the endpoint below, which is the normal MCP flow. + */ +export default defineEventHandler(async (event) => { + const config = useRuntimeConfig() + const site = `${config.public.canonicalUrl}${config.public.baseUrl}` + + let definitions: { + tools: { name: string, description?: string }[] + resources: { name: string, uri: string, description?: string }[] + prompts: { name: string, description?: string }[] + } | undefined + + try { + const { listMcpDefinitions } = await import('@nuxtjs/mcp-toolkit/server') + definitions = await listMcpDefinitions({ event }) as typeof definitions + } catch { + // Prerender: the virtual tools module is not resolvable. Fall through. + } + + setResponseHeader(event, 'Content-Type', 'application/json; charset=utf-8') + + return { + $schema: 'https://modelcontextprotocol.io/schema/server-card/v1', + serverInfo: { + name: 'Bitrix24 UI', + version: config.public.version, + title: 'Bitrix24 UI MCP Server', + description: 'MCP server providing tools, resources and prompts to help AI agents build with Bitrix24 UI — search components and composables, retrieve documentation, fetch component metadata, and list starter templates.', + homepage: site, + documentation: `${site}/docs/getting-started/`, + license: 'MIT', + repository: 'https://github.com/bitrix24/b24ui' + }, + endpoints: [ + { + type: 'streamable-http', + url: `${site}/mcp/` + } + ], + capabilities: { + tools: { listChanged: false }, + resources: { listChanged: false, subscribe: false }, + prompts: { listChanged: false }, + logging: {} + }, + ...(definitions + ? { + tools: definitions.tools.map(tool => ({ name: tool.name, description: tool.description })), + resources: definitions.resources.map(resource => ({ name: resource.name, uri: resource.uri, description: resource.description })), + prompts: definitions.prompts.map(prompt => ({ name: prompt.name, description: prompt.description })) + } + : {}), + authentication: { + required: false + } + } +})