MCP Server
Turn on mcp to write a full-text, untruncated site/mcp-index.json (plus site/mcp-manifest.json and a per-tree mcp-nav.json) on every build, so bxSites Cloud can expose your published site through a public, read-only MCP server for AI agents - independent of search/searchProvider.
On this page
MCP Server
Turn mcp: true on in bxsites.yaml and every build writes three files
alongside your rendered pages - a full-text, machine-readable copy of your
site's content, nav, and tree layout that
bxSites Cloud reads to expose a public,
read-only MCP server for your
published site, the same idea as
GitBook's "MCP servers for published docs".
Once published, AI agents and assistants (Claude, ChatGPT, and any other
MCP-aware client) can search, browse, and retrieve your site's content
directly, instead of scraping rendered HTML:
site/mcp-index.json- one full-text entry per page, per treesite/mcp-nav.json- that tree's own nav structure, per treesite/mcp-manifest.json- one site-wide index of every tree (main site, versions, locales) and where to find each one's own files above
BxSites itself only ever produces the files described on this page - actually serving them as an MCP server over the network is bxSites Cloud's job, not something this module does on its own.
Building mcp-index.json itself is free and works with bxSites build
alone, no Cloud account required. Actually exposing it as a live MCP
server for a published site is a bxSites Cloud feature available on its
paid plans only - it isn't included on the free plan. See
bxsites.io/cloud for current plan details.
Turning it on
mcp: true
{ "mcp": true }
mcp = true
false (the default) skips the whole step - none of mcp-index.json,
mcp-nav.json, or mcp-manifest.json are written, and build pays no
extra cost beyond checking the flag.
Independent of search
mcp has nothing to do with search/searchProvider - the
two are unrelated switches:
mcp-index.jsonis generated regardless of whethersearchistrueorfalse, and regardless ofsearchProvider.provider("local","algolia","pagefind", or a custom one).- Turning
mcpon never changes anything about your site's own visitor- facing search box, and turningsearchoff never disablesmcp-index.json.
This is why mcp-index.json can't simply reuse search-index.json: that
file is only ever built for the "local" search provider (an
Algolia- or Pagefind-backed site produces no search-index.json at all,
since each of those keeps its own index elsewhere), and its body field
is deliberately truncated - search-index.json ships to every visitor's
browser for the in-page search box, so keeping it small matters. Neither
constraint applies to mcp-index.json: it's fetched server-side by
bxSites Cloud, not shipped to visitors, and a truncated body risks an AI
agent giving an incomplete or wrong answer.
The mcp-index.json format
One entry per non-hidden page (the same "hidden pages are excluded"
convention search-index.json/the nav both use), covering both regular
docs pages and blog posts:
[
{
"title": "Getting Started",
"url": "getting-started/index.html",
"tags": ["guides"],
"headings": ["Getting Started", "Installation", "Next steps"],
"body": "Getting Started Installation Run bxSites new to scaffold a project... Next steps ...",
"markdown": "# Getting Started\n\n## Installation\n\nRun `bxSites new` to scaffold a project...\n\n## Next steps\n\n...",
"type": "page",
"categories": [],
"updatedAt": "2026-08-18T10:15:00.000Z"
},
{
"title": "Announcing bxSites 2.0",
"url": "blog/announcing-bxsites-2/index.html",
"tags": ["release"],
"headings": ["Announcing bxSites 2.0"],
"body": "Announcing bxSites 2.0 Today we're shipping...",
"markdown": "# Announcing bxSites 2.0\n\nToday we're shipping...",
"type": "post",
"categories": ["Releases"],
"updatedAt": "2026-08-15"
}
]
| Field | Description |
|---|---|
title | The page's title (frontmatter title, same as search-index.json) |
url | The page's URL path, relative to the root of its own tree - never root-absolute, never carrying the tree's own path (see Tree-relative URLs below) |
tags | The page's frontmatter tags array |
headings | The plain text of every h1-h6 on the page, in document order |
body | The complete plain-text page content, HTML tags stripped - never truncated |
markdown | The same page content converted to Markdown from its rendered HTML, never truncated (see Markdown content below). Missing from an index built by an older bxSites - fall back to body there. |
type | "page" for a regular docs page, "post" for a blog post |
categories | A post's own frontmatter categories array; always [] for a docs page (docs pages don't have categories) |
updatedAt | A post's own frontmatter date; a docs page's own frontmatter date when it sets one, else its source file's last-modified time as an ISO-8601 instant. "" when neither is available. |
Aside from markdown, type, categories and updatedAt, this is the same entry shape
search-index.json uses, with one difference: there, body is cut to 400
characters with a trailing ellipsis; here, it's the full page.
Markdown content
markdown is the page's rendered content converted to Markdown, so an AI
agent can read headings, code, lists and tables without parsing HTML. It is
converted from the rendered HTML, not from the source file (which can contain
build directives and variables), so it matches what visitors see. body is
unchanged and stays the plain text used for search.
What the conversion produces:
- Headings as ATX (
# Title,## Section), without the anchor links - Fenced code blocks keep their language when the rendered code has one
(a
boxlangfence); a block with no language comes out as an indented code block - Lists, nested lists, tables and inline formatting
- Tab groups: each panel is prefixed with its tab label in bold (
**YAML**) - Admonitions: a blockquote whose first line is the title in bold
- Images as
; decorative images with empty alt text are dropped - Scripts, styles, svg sprites and form controls are removed
- Links and image sources are kept exactly as rendered: site-absolute paths
that already carry the build's
baseUrlpath and the tree's own prefix (for example/versions/0.9/guides/setup/index.html)
markdown roughly doubles the size of mcp-index.json. On this
documentation site the main tree's 73 pages went from about 460 KB to
about 1 MB.
Tree-relative URLs
mcp-index.json is written once per tree, and each entry's url is relative
to that tree's own root: no leading slash, no tree prefix, no baseUrl path.
A page's full path is the tree's manifest path joined with its url (just
the url when path is ""):
Tree (manifest path) | Index file | Entry url | Page file |
|---|---|---|---|
"" | mcp-index.json | guides/setup/index.html | guides/setup/index.html |
versions/0.9 | versions/0.9/mcp-index.json | guides/setup/index.html | versions/0.9/guides/setup/index.html |
es | es/mcp-index.json | guides/setup/index.html | es/guides/setup/index.html |
So the same url appears in every tree that has that page. Anything that
matches an entry against a site-absolute request path, such as a
password-gated subtree, must prepend the tree's path first. This only
applies to url: the links and image sources inside markdown are already
site-absolute.
The mcp-manifest.json format
Written once per site build (not once per tree), site/mcp-manifest.json
lists every tree that got its own mcp-index.json/mcp-nav.json, so
bxSites Cloud's MCP server can offer version-and-locale-aware tools
without guessing bx-sites' own directory conventions:
[
{ "path": "", "label": "1.0.x", "version": "1.0.x", "locale": "en", "default": true },
{ "path": "next", "label": "Next", "version": null, "locale": "en", "default": false },
{ "path": "versions/0.9", "label": "0.9", "version": "0.9", "locale": "en", "default": false },
{ "path": "es", "label": "Español", "version": "1.0.x", "locale": "es", "default": false }
]
| Field | Description |
|---|---|
path | This tree's own root-relative path - "" for the main tree at the site root, "next"/"versions/<name>"/"<localeCode>"/"versions/<name>/<localeCode>" otherwise. Join with /mcp-index.json (or /mcp-nav.json) to get that tree's own file, e.g. "versions/0.9/mcp-index.json" - and just "mcp-index.json" for the root's own (path is "" there). |
label | A human-readable label for this tree - the version switcher's own label (versions.default's name, a plain version's own name, or "Latest"/"Next"), combined with the locale's own label for a locale sub-tree |
version | The docs/versions/<name>/ name this tree renders, or null when it isn't a named version (the unversioned main tree, or /next/) |
locale | This tree's own locale code - i18n.defaultLocale.code for a tree with no locale suffix, otherwise that locale's own code |
default | true for the one tree that renders at the site root - the one a visitor/AI agent lands on with no version or locale specified |
The mcp-nav.json format
Written alongside every tree's own mcp-index.json (site root, /next/,
each /versions/<name>/ tree, each locale sub-tree), mcp-nav.json is
that tree's own nav - the exact same nested { title, url, order, icon, children } structure the theme itself renders the sidebar from - so
bxSites Cloud's MCP server can offer a get_nav/table-of-contents tool
without inventing a second nav shape of its own:
[
{
"title": "Getting Started",
"url": "getting-started/index.html",
"order": 1,
"icon": "phosphor-duotone:rocket-launch",
"children": []
},
{
"title": "Guides",
"url": "",
"order": 2,
"icon": "",
"children": [
{ "title": "Search", "url": "guides/search/index.html", "order": 1, "icon": "", "children": [] }
]
}
]
A folder-group node with no index.md of its own (like "Guides" above)
has an empty url - it's not a page, just a heading for its children.
Multi-version and multi-locale sites
Just like search-index.json, mcp-index.json/mcp-nav.json are written
once per rendered tree - the main site, /next/ (when versions.default
is set), each /versions/<name>/ tree, and each locale sub-tree - so
every published tree gets its own pair of files covering just that tree's
own pages and nav. mcp-manifest.json, on the other hand, is written
once per build, at the site root only, listing every one of those trees.