blume 1.6.1 → 1.6.3
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/CHANGELOG.md +12 -0
- package/dist/cli/index.js +907 -152
- package/dist/cli/index.js.map +20 -16
- package/dist/types/core/config-input.d.ts +9 -0
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/schema.d.ts +2 -0
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/configuration/ai.mdx +72 -7
- package/docs/content/components.mdx +1 -1
- package/docs/index.mdx +2 -2
- package/package.json +1 -1
- package/skills/blume/SKILL.md +2 -2
- package/src/ai/agent-readability.ts +60 -17
- package/src/ai/api/handlers.ts +273 -0
- package/src/ai/api/paths.ts +14 -0
- package/src/ai/api/problem.ts +63 -0
- package/src/ai/api/spec.ts +681 -0
- package/src/ai/api-catalog.ts +11 -1
- package/src/ai/link-headers.ts +12 -3
- package/src/ai/llms.ts +9 -2
- package/src/ai/mcp/query.ts +390 -0
- package/src/ai/mcp/server.ts +32 -352
- package/src/astro/generate.ts +166 -12
- package/src/astro/templates.ts +157 -0
- package/src/cli/commands/build.ts +8 -6
- package/src/components/content/Component.astro +5 -9
- package/src/components/content/Tabs.astro +24 -9
- package/src/components/layout/RootLayout.astro +105 -36
- package/src/core/config-input.ts +9 -0
- package/src/core/data.ts +7 -1
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/schema.ts +7 -0
- package/src/deploy/vercel-negotiation.ts +56 -8
- package/src/theme/code-block-padding.ts +0 -8
- package/src/theme/entry.ts +14 -25
package/dist/cli/index.js
CHANGED
|
@@ -3267,6 +3267,14 @@ var builtinProxyKinds = (config) => {
|
|
|
3267
3267
|
};
|
|
3268
3268
|
var needsPlaygroundProxy = (config) => builtinProxyKinds(config).length > 0;
|
|
3269
3269
|
|
|
3270
|
+
// src/ai/api/paths.ts
|
|
3271
|
+
var OPENAPI_PATH = "/openapi.json";
|
|
3272
|
+
var API_BASE = "/api/docs";
|
|
3273
|
+
var API_PAGES_PATH = `${API_BASE}/pages.json`;
|
|
3274
|
+
var API_PAGE_PATH = `${API_BASE}/pages/{route}.json`;
|
|
3275
|
+
var API_NAVIGATION_PATH = `${API_BASE}/navigation.json`;
|
|
3276
|
+
var API_SEARCH_PATH = `${API_BASE}/search`;
|
|
3277
|
+
|
|
3270
3278
|
// src/ai/api-catalog.ts
|
|
3271
3279
|
var API_CATALOG_PATH = "/.well-known/api-catalog";
|
|
3272
3280
|
var API_CATALOG_TYPE = "application/linkset+json";
|
|
@@ -3290,6 +3298,13 @@ var linksetEntries = (config) => {
|
|
|
3290
3298
|
}
|
|
3291
3299
|
entries.push(entry);
|
|
3292
3300
|
}
|
|
3301
|
+
if (config.ai.api) {
|
|
3302
|
+
entries.push({
|
|
3303
|
+
anchor: abs(API_BASE),
|
|
3304
|
+
"service-desc": [{ href: abs(OPENAPI_PATH), type: "application/json" }],
|
|
3305
|
+
"service-doc": [{ href: abs("/"), type: "text/html" }]
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3293
3308
|
if (config.ai.mcp.enabled) {
|
|
3294
3309
|
entries.push({
|
|
3295
3310
|
anchor: abs(config.ai.mcp.route),
|
|
@@ -3318,6 +3333,9 @@ var buildHomeLinkHeader = (config, routePaths) => {
|
|
|
3318
3333
|
if (hasApiCatalog(config)) {
|
|
3319
3334
|
links.push(`<${deployBase}${API_CATALOG_PATH}>; rel="api-catalog"; type="application/linkset+json"`);
|
|
3320
3335
|
}
|
|
3336
|
+
if (config.ai.api) {
|
|
3337
|
+
links.push(`<${deployBase}${OPENAPI_PATH}>; rel="service-desc"; type="application/json"`);
|
|
3338
|
+
}
|
|
3321
3339
|
if (config.seo.agentReadability) {
|
|
3322
3340
|
links.push(`<${deployBase}/agent-readability.json>; rel="describedby"; type="application/json"`);
|
|
3323
3341
|
}
|
|
@@ -5662,6 +5680,9 @@ const links = [
|
|
|
5662
5680
|
...(data.config.discovery.llmsTxt
|
|
5663
5681
|
? [{ href: href("/llms.txt"), label: nf.llms }]
|
|
5664
5682
|
: []),
|
|
5683
|
+
...(data.config.discovery.api
|
|
5684
|
+
? [{ href: href("/openapi.json"), label: nf.api }]
|
|
5685
|
+
: []),
|
|
5665
5686
|
];
|
|
5666
5687
|
|
|
5667
5688
|
const body = [
|
|
@@ -5685,6 +5706,114 @@ export function GET() {
|
|
|
5685
5706
|
});
|
|
5686
5707
|
}
|
|
5687
5708
|
`;
|
|
5709
|
+
var notFoundJsonTemplate = () => `// Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
|
|
5710
|
+
import { problem } from "blume/ai/api/problem.ts";
|
|
5711
|
+
import { withBase } from "blume/components/islands/base-path.ts";
|
|
5712
|
+
import { absoluteUrl } from "blume/core/site-url.ts";
|
|
5713
|
+
import data from "blume:data";
|
|
5714
|
+
|
|
5715
|
+
export const prerender = true;
|
|
5716
|
+
|
|
5717
|
+
const nf = data.ui.notFound;
|
|
5718
|
+
|
|
5719
|
+
// Absolute for internal routes when the site is known; an external tab href
|
|
5720
|
+
// passes through untouched.
|
|
5721
|
+
const href = (path: string): string => {
|
|
5722
|
+
const based = withBase(path);
|
|
5723
|
+
return data.config.site && based.startsWith("/") && !based.startsWith("//")
|
|
5724
|
+
? absoluteUrl(data.config.site, based)
|
|
5725
|
+
: based;
|
|
5726
|
+
};
|
|
5727
|
+
|
|
5728
|
+
// The recovery set of 404.astro: home, every top-level section (a tab links to
|
|
5729
|
+
// its resolved target), then the machine-readable indexes that exist.
|
|
5730
|
+
const links = [
|
|
5731
|
+
{ href: href("/"), label: nf.home },
|
|
5732
|
+
...data.navigation.tabs.map((tab) => ({
|
|
5733
|
+
href: href(tab.href ?? tab.path),
|
|
5734
|
+
label: tab.label,
|
|
5735
|
+
})),
|
|
5736
|
+
...(data.config.discovery.sitemap
|
|
5737
|
+
? [{ href: href("/sitemap.xml"), label: nf.sitemap }]
|
|
5738
|
+
: []),
|
|
5739
|
+
...(data.config.discovery.llmsTxt
|
|
5740
|
+
? [{ href: href("/llms.txt"), label: nf.llms }]
|
|
5741
|
+
: []),
|
|
5742
|
+
...(data.config.discovery.api
|
|
5743
|
+
? [{ href: href("/openapi.json"), label: nf.api }]
|
|
5744
|
+
: []),
|
|
5745
|
+
];
|
|
5746
|
+
|
|
5747
|
+
const body = problem({
|
|
5748
|
+
code: "PAGE_NOT_FOUND",
|
|
5749
|
+
detail: nf.description,
|
|
5750
|
+
links,
|
|
5751
|
+
resolution: nf.suggestions + ": " + links.map((link) => link.href).join(", "),
|
|
5752
|
+
status: 404,
|
|
5753
|
+
title: nf.title,
|
|
5754
|
+
});
|
|
5755
|
+
|
|
5756
|
+
export function GET() {
|
|
5757
|
+
return new Response(JSON.stringify(body, null, 2) + "\\n", {
|
|
5758
|
+
headers: { "Content-Type": "application/problem+json; charset=utf-8" },
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
`;
|
|
5762
|
+
var apiPagesIndexTemplate = () => `// Generated by Blume. Do not edit.
|
|
5763
|
+
import { pagesIndexResponse } from "blume/ai/api/handlers.ts";
|
|
5764
|
+
import data from "blume:mcp-data";
|
|
5765
|
+
|
|
5766
|
+
export const prerender = true;
|
|
5767
|
+
|
|
5768
|
+
export function GET() {
|
|
5769
|
+
return pagesIndexResponse(data);
|
|
5770
|
+
}
|
|
5771
|
+
`;
|
|
5772
|
+
var apiPageTemplate = () => `// Generated by Blume. Do not edit.
|
|
5773
|
+
import { pageParams, pageResponse } from "blume/ai/api/handlers.ts";
|
|
5774
|
+
import data from "blume:mcp-data";
|
|
5775
|
+
|
|
5776
|
+
export const prerender = true;
|
|
5777
|
+
|
|
5778
|
+
export function getStaticPaths() {
|
|
5779
|
+
return pageParams(data);
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
export function GET({ props }: { props: { route: string } }) {
|
|
5783
|
+
return pageResponse(data, props.route);
|
|
5784
|
+
}
|
|
5785
|
+
`;
|
|
5786
|
+
var apiNavigationTemplate = () => `// Generated by Blume. Do not edit.
|
|
5787
|
+
import { navigationResponse } from "blume/ai/api/handlers.ts";
|
|
5788
|
+
import data from "blume:mcp-data";
|
|
5789
|
+
|
|
5790
|
+
export const prerender = true;
|
|
5791
|
+
|
|
5792
|
+
export function GET() {
|
|
5793
|
+
return navigationResponse(data);
|
|
5794
|
+
}
|
|
5795
|
+
`;
|
|
5796
|
+
var apiSearchTemplate = () => `// Generated by Blume. Do not edit.
|
|
5797
|
+
import type { APIRoute } from "astro";
|
|
5798
|
+
import { createSearchHandler } from "blume/ai/api/handlers.ts";
|
|
5799
|
+
import data from "blume:mcp-data";
|
|
5800
|
+
|
|
5801
|
+
export const prerender = false;
|
|
5802
|
+
|
|
5803
|
+
const handler = createSearchHandler(data);
|
|
5804
|
+
|
|
5805
|
+
export const GET: APIRoute = ({ request }) => handler(request);
|
|
5806
|
+
`;
|
|
5807
|
+
var apiNotFoundTemplate = (context) => `// Generated by Blume. Do not edit.
|
|
5808
|
+
import type { APIRoute } from "astro";
|
|
5809
|
+
import { apiNotFoundResponse } from "blume/ai/api/handlers.ts";
|
|
5810
|
+
|
|
5811
|
+
export const prerender = false;
|
|
5812
|
+
|
|
5813
|
+
const context = ${JSON.stringify(context)};
|
|
5814
|
+
|
|
5815
|
+
export const ALL: APIRoute = ({ request }) => apiNotFoundResponse(request, context);
|
|
5816
|
+
`;
|
|
5688
5817
|
var islandDirective = (spec) => spec.client === "only" ? `client:only="${spec.framework}"` : `client:${spec.client}`;
|
|
5689
5818
|
var wrapperPropsType = (name) => `type Props = typeof ${name} extends (
|
|
5690
5819
|
props: infer P extends object,
|
|
@@ -10091,6 +10220,7 @@ var uiStringsObject = z.object({
|
|
|
10091
10220
|
toggleTheme: z.string().default("Toggle color theme")
|
|
10092
10221
|
}).prefault({}),
|
|
10093
10222
|
notFound: z.object({
|
|
10223
|
+
api: z.string().default("JSON API description (openapi.json)"),
|
|
10094
10224
|
description: z.string().default("We couldn't find the page you're looking for."),
|
|
10095
10225
|
home: z.string().default("Back to home"),
|
|
10096
10226
|
llms: z.string().default("Docs index for AI agents (llms.txt)"),
|
|
@@ -10597,6 +10727,7 @@ var llmsTxtObjectSchema = z2.strictObject({
|
|
|
10597
10727
|
openapi: z2.boolean().default(true)
|
|
10598
10728
|
});
|
|
10599
10729
|
var aiConfigSchema = z2.strictObject({
|
|
10730
|
+
api: z2.boolean().default(true),
|
|
10600
10731
|
ask: z2.strictObject({
|
|
10601
10732
|
apiKeyEnv: z2.string().optional(),
|
|
10602
10733
|
baseUrl: z2.url().optional(),
|
|
@@ -16381,6 +16512,23 @@ var askApiUrl = (endpoint, site, abs) => {
|
|
|
16381
16512
|
}
|
|
16382
16513
|
return site && endpoint.startsWith("/") ? absoluteUrl(site, endpoint) : endpoint;
|
|
16383
16514
|
};
|
|
16515
|
+
var markdownArtifact = (config, abs) => {
|
|
16516
|
+
const negotiates = config.deployment.output === "server" && (config.deployment.adapter === "vercel" || config.deployment.adapter === "cloudflare");
|
|
16517
|
+
return negotiates ? { contentNegotiation: "text/markdown", pattern: abs("/{route}.md") } : { pattern: abs("/{route}.md") };
|
|
16518
|
+
};
|
|
16519
|
+
var apiArtifact = (config, abs) => {
|
|
16520
|
+
if (!config.ai.api) {
|
|
16521
|
+
return null;
|
|
16522
|
+
}
|
|
16523
|
+
const api = {
|
|
16524
|
+
openapi: abs(OPENAPI_PATH),
|
|
16525
|
+
pages: abs(API_PAGES_PATH)
|
|
16526
|
+
};
|
|
16527
|
+
if (config.deployment.output === "server") {
|
|
16528
|
+
api.search = abs(API_SEARCH_PATH);
|
|
16529
|
+
}
|
|
16530
|
+
return api;
|
|
16531
|
+
};
|
|
16384
16532
|
var wellKnownArtifacts = (config, abs) => {
|
|
16385
16533
|
const artifacts = {};
|
|
16386
16534
|
if (config.ai.webBotAuth.keys.length > 0) {
|
|
@@ -16405,9 +16553,13 @@ var buildAgentReadability = (project) => {
|
|
|
16405
16553
|
const based = withBasePath(deployBase, path);
|
|
16406
16554
|
return site ? absoluteUrl(site, based) : based;
|
|
16407
16555
|
};
|
|
16408
|
-
const
|
|
16409
|
-
|
|
16410
|
-
|
|
16556
|
+
const artifacts = {
|
|
16557
|
+
markdown: markdownArtifact(config, abs)
|
|
16558
|
+
};
|
|
16559
|
+
const api = apiArtifact(config, abs);
|
|
16560
|
+
if (api) {
|
|
16561
|
+
artifacts.api = api;
|
|
16562
|
+
}
|
|
16411
16563
|
if (config.ai.llmsTxt.enabled) {
|
|
16412
16564
|
artifacts.llmsFullTxt = abs("/llms-full.txt");
|
|
16413
16565
|
artifacts.llmsTxt = abs("/llms.txt");
|
|
@@ -17123,6 +17275,9 @@ var agentResourceLines = (project) => {
|
|
|
17123
17275
|
`- [llms-full.txt](${url("/llms-full.txt")}): The full Markdown of every page in one file.`,
|
|
17124
17276
|
`- [Page Markdown](${url("/index.md")}): Append \`.md\` to any page URL to fetch that page as raw Markdown.`
|
|
17125
17277
|
];
|
|
17278
|
+
if (config.ai.api) {
|
|
17279
|
+
lines.push(`- [JSON API](${url(API_PAGES_PATH)}): Page index of the JSON docs API; each entry links the page's JSON and Markdown forms. Described by the OpenAPI document at ${url(OPENAPI_PATH)}.`);
|
|
17280
|
+
}
|
|
17126
17281
|
if (config.ai.mcp.enabled) {
|
|
17127
17282
|
lines.push(`- [MCP server](${url(config.ai.mcp.route)}): Streamable HTTP Model Context Protocol server with search_docs, get_page, list_pages, and get_navigation tools, plus every page as a resource. Discovery document: ${url("/.well-known/mcp.json")}`);
|
|
17128
17283
|
}
|
|
@@ -18062,12 +18217,17 @@ ${references.join(`
|
|
|
18062
18217
|
|
|
18063
18218
|
// src/deploy/vercel-negotiation.ts
|
|
18064
18219
|
var ACCEPT_MARKDOWN_HEADER_VALUE = "(.*,)?\\s*text/(x-)?markdown(\\s*[;,].*)?$";
|
|
18220
|
+
var ACCEPT_JSON_HEADER_VALUE = "(.*,)?\\s*application/(problem\\+)?json(\\s*[;,].*)?$";
|
|
18065
18221
|
var isString6 = (value) => typeof value === "string";
|
|
18066
18222
|
var ACCEPT_MARKDOWN_CONDITION = [
|
|
18067
18223
|
{ key: "accept", type: "header", value: ACCEPT_MARKDOWN_HEADER_VALUE }
|
|
18068
18224
|
];
|
|
18225
|
+
var ACCEPT_JSON_CONDITION = [
|
|
18226
|
+
{ key: "accept", type: "header", value: ACCEPT_JSON_HEADER_VALUE }
|
|
18227
|
+
];
|
|
18069
18228
|
var VARY_ACCEPT = { vary: "Accept" };
|
|
18070
18229
|
var NOT_FOUND_MARKDOWN_DEST = "/404.md";
|
|
18230
|
+
var NOT_FOUND_JSON_DEST = "/404.json";
|
|
18071
18231
|
var NOT_FOUND_HTML_DEST = "/404.html";
|
|
18072
18232
|
var NOT_FOUND_MARKDOWN_ROUTES = [
|
|
18073
18233
|
{
|
|
@@ -18079,6 +18239,16 @@ var NOT_FOUND_MARKDOWN_ROUTES = [
|
|
|
18079
18239
|
},
|
|
18080
18240
|
{ dest: NOT_FOUND_MARKDOWN_DEST, src: "^/.*\\.mdx?$", status: 404 }
|
|
18081
18241
|
];
|
|
18242
|
+
var NOT_FOUND_JSON_ROUTES = [
|
|
18243
|
+
{
|
|
18244
|
+
dest: NOT_FOUND_JSON_DEST,
|
|
18245
|
+
has: ACCEPT_JSON_CONDITION,
|
|
18246
|
+
headers: VARY_ACCEPT,
|
|
18247
|
+
src: "^/.*$",
|
|
18248
|
+
status: 404
|
|
18249
|
+
},
|
|
18250
|
+
{ dest: NOT_FOUND_JSON_DEST, src: "^/.*\\.json$", status: 404 }
|
|
18251
|
+
];
|
|
18082
18252
|
var MAX_ALTERNATION_LENGTH = 3900;
|
|
18083
18253
|
var REGEX_SPECIALS = /[$()*+.?[\]^{|}\\]/gu;
|
|
18084
18254
|
var routePattern = (route) => encodeURI(route).replace(REGEX_SPECIALS, "\\$&");
|
|
@@ -18134,8 +18304,8 @@ var TRAILING_SLASH_REDIRECT = {
|
|
|
18134
18304
|
src: "^/(.+)/$",
|
|
18135
18305
|
status: 308
|
|
18136
18306
|
};
|
|
18137
|
-
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.dest === NOT_FOUND_MARKDOWN_DEST && route.status === 404 || route.continue === true && route.headers?.vary === "Accept" && isString6(route.src) && Object.keys(route).length === 3 || route.continue === true && isString6(route.headers?.link) && route.src === HOME_SRC && Object.keys(route).length === 3 || route.status === TRAILING_SLASH_REDIRECT.status && route.src === TRAILING_SLASH_REDIRECT.src;
|
|
18138
|
-
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens,
|
|
18307
|
+
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.dest === NOT_FOUND_MARKDOWN_DEST && route.status === 404 || route.dest === NOT_FOUND_JSON_DEST && route.status === 404 || route.continue === true && route.headers?.vary === "Accept" && isString6(route.src) && Object.keys(route).length === 3 || route.continue === true && isString6(route.headers?.link) && route.src === HOME_SRC && Object.keys(route).length === 3 || route.status === TRAILING_SLASH_REDIRECT.status && route.src === TRAILING_SLASH_REDIRECT.src;
|
|
18308
|
+
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens, notFound = {}) => {
|
|
18139
18309
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
18140
18310
|
let config;
|
|
18141
18311
|
try {
|
|
@@ -18163,10 +18333,14 @@ var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTy
|
|
|
18163
18333
|
});
|
|
18164
18334
|
}
|
|
18165
18335
|
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes, TRAILING_SLASH_REDIRECT);
|
|
18166
|
-
|
|
18336
|
+
const notFoundRoutes = [
|
|
18337
|
+
...notFound.markdown ? NOT_FOUND_MARKDOWN_ROUTES : [],
|
|
18338
|
+
...notFound.json ? NOT_FOUND_JSON_ROUTES : []
|
|
18339
|
+
];
|
|
18340
|
+
if (notFoundRoutes.length > 0) {
|
|
18167
18341
|
const fallbackIndex = routes.findIndex((route) => route.status === 404 && route.dest === NOT_FOUND_HTML_DEST);
|
|
18168
18342
|
if (fallbackIndex !== -1) {
|
|
18169
|
-
routes.splice(fallbackIndex, 0, ...
|
|
18343
|
+
routes.splice(fallbackIndex, 0, ...notFoundRoutes);
|
|
18170
18344
|
}
|
|
18171
18345
|
}
|
|
18172
18346
|
config.routes = routes;
|
|
@@ -18731,6 +18905,519 @@ import {
|
|
|
18731
18905
|
} from "pathe";
|
|
18732
18906
|
import { glob as glob7 } from "tinyglobby";
|
|
18733
18907
|
|
|
18908
|
+
// src/ai/api/problem.ts
|
|
18909
|
+
var PROBLEM_TYPE = "application/problem+json";
|
|
18910
|
+
|
|
18911
|
+
// src/ai/api/spec.ts
|
|
18912
|
+
var JSON_TYPE = "application/json";
|
|
18913
|
+
var MARKDOWN_TYPE = "text/markdown";
|
|
18914
|
+
var TEXT_TYPE = "text/plain";
|
|
18915
|
+
var ref = (name) => ({
|
|
18916
|
+
$ref: `#/components/schemas/${name}`
|
|
18917
|
+
});
|
|
18918
|
+
var jsonResponse = (description, schema) => ({
|
|
18919
|
+
content: { [JSON_TYPE]: { schema: ref(schema) } },
|
|
18920
|
+
description
|
|
18921
|
+
});
|
|
18922
|
+
var problemResponse = (description) => ({
|
|
18923
|
+
content: { [PROBLEM_TYPE]: { schema: ref("Problem") } },
|
|
18924
|
+
description
|
|
18925
|
+
});
|
|
18926
|
+
var textResponse = (description, type) => ({
|
|
18927
|
+
content: { [type]: { schema: { type: "string" } } },
|
|
18928
|
+
description
|
|
18929
|
+
});
|
|
18930
|
+
var string = (description) => ({ description, type: "string" });
|
|
18931
|
+
var facetsSchema = {
|
|
18932
|
+
additionalProperties: { type: "string" },
|
|
18933
|
+
description: "Facet values the site declares for the page's content type (`content.types.<type>.facets`), key → value.",
|
|
18934
|
+
type: "object"
|
|
18935
|
+
};
|
|
18936
|
+
var versionProperty = {
|
|
18937
|
+
description: 'Docs version the page belongs to on a versioned site: `""` for the current docs, else an archived version id. Absent on unversioned sites.',
|
|
18938
|
+
type: "string"
|
|
18939
|
+
};
|
|
18940
|
+
var searchParameters = [
|
|
18941
|
+
{
|
|
18942
|
+
description: "The search query.",
|
|
18943
|
+
in: "query",
|
|
18944
|
+
name: "q",
|
|
18945
|
+
required: true,
|
|
18946
|
+
schema: { minLength: 1, type: "string" }
|
|
18947
|
+
},
|
|
18948
|
+
{
|
|
18949
|
+
description: "Maximum hits to return (default 8, at most 20).",
|
|
18950
|
+
in: "query",
|
|
18951
|
+
name: "limit",
|
|
18952
|
+
required: false,
|
|
18953
|
+
schema: { default: 8, maximum: 20, minimum: 1, type: "integer" }
|
|
18954
|
+
},
|
|
18955
|
+
{
|
|
18956
|
+
description: "Only include pages of these content types (frontmatter `type`, e.g. `doc`, `rfc`). Comma-separated or repeated. Omit for every type.",
|
|
18957
|
+
explode: false,
|
|
18958
|
+
in: "query",
|
|
18959
|
+
name: "contentTypes",
|
|
18960
|
+
required: false,
|
|
18961
|
+
schema: { items: { type: "string" }, type: "array" },
|
|
18962
|
+
style: "form"
|
|
18963
|
+
},
|
|
18964
|
+
{
|
|
18965
|
+
description: "Only include pages in this locale (e.g. `fr`). Omit for every language.",
|
|
18966
|
+
in: "query",
|
|
18967
|
+
name: "locale",
|
|
18968
|
+
required: false,
|
|
18969
|
+
schema: { type: "string" }
|
|
18970
|
+
},
|
|
18971
|
+
{
|
|
18972
|
+
description: "Docs version to scope to on a versioned site: `latest` (the default — current docs only), `all`, or an archived version id. Ignored when the site is unversioned.",
|
|
18973
|
+
in: "query",
|
|
18974
|
+
name: "version",
|
|
18975
|
+
required: false,
|
|
18976
|
+
schema: { type: "string" }
|
|
18977
|
+
},
|
|
18978
|
+
{
|
|
18979
|
+
description: "Only include pages matching every facet, as `filters[key]=value` pairs (e.g. `filters[status]=enforced`). Facets are metadata the site declares per content type; the page index shows each page's values.",
|
|
18980
|
+
explode: true,
|
|
18981
|
+
in: "query",
|
|
18982
|
+
name: "filters",
|
|
18983
|
+
required: false,
|
|
18984
|
+
schema: { additionalProperties: { type: "string" }, type: "object" },
|
|
18985
|
+
style: "deepObject"
|
|
18986
|
+
}
|
|
18987
|
+
];
|
|
18988
|
+
var schemas = {
|
|
18989
|
+
JsonRpcRequest: {
|
|
18990
|
+
description: "A JSON-RPC 2.0 request, as the Model Context Protocol sends.",
|
|
18991
|
+
properties: {
|
|
18992
|
+
id: { description: "Request id (absent on notifications)." },
|
|
18993
|
+
jsonrpc: { const: "2.0", type: "string" },
|
|
18994
|
+
method: string("The MCP method, e.g. `initialize` or `tools/call`."),
|
|
18995
|
+
params: { description: "Method parameters.", type: "object" }
|
|
18996
|
+
},
|
|
18997
|
+
required: ["jsonrpc", "method"],
|
|
18998
|
+
type: "object"
|
|
18999
|
+
},
|
|
19000
|
+
JsonRpcResponse: {
|
|
19001
|
+
description: "A JSON-RPC 2.0 response: a `result` or an `error`.",
|
|
19002
|
+
properties: {
|
|
19003
|
+
error: {
|
|
19004
|
+
properties: {
|
|
19005
|
+
code: { type: "integer" },
|
|
19006
|
+
data: {},
|
|
19007
|
+
message: { type: "string" }
|
|
19008
|
+
},
|
|
19009
|
+
required: ["code", "message"],
|
|
19010
|
+
type: "object"
|
|
19011
|
+
},
|
|
19012
|
+
id: {},
|
|
19013
|
+
jsonrpc: { const: "2.0", type: "string" },
|
|
19014
|
+
result: { type: "object" }
|
|
19015
|
+
},
|
|
19016
|
+
required: ["jsonrpc"],
|
|
19017
|
+
type: "object"
|
|
19018
|
+
},
|
|
19019
|
+
NavLink: {
|
|
19020
|
+
properties: {
|
|
19021
|
+
href: string("Link target — an internal route or an external URL."),
|
|
19022
|
+
icon: string("Optional icon name."),
|
|
19023
|
+
label: { type: "string" }
|
|
19024
|
+
},
|
|
19025
|
+
required: ["href", "label"],
|
|
19026
|
+
type: "object"
|
|
19027
|
+
},
|
|
19028
|
+
NavNode: {
|
|
19029
|
+
description: "A sidebar entry: a page (linking to its route) or a group holding further nodes.",
|
|
19030
|
+
oneOf: [
|
|
19031
|
+
{
|
|
19032
|
+
properties: {
|
|
19033
|
+
badge: { type: "string" },
|
|
19034
|
+
deprecated: { type: "boolean" },
|
|
19035
|
+
description: { type: "string" },
|
|
19036
|
+
icon: { type: "string" },
|
|
19037
|
+
kind: { const: "page", type: "string" },
|
|
19038
|
+
label: { type: "string" },
|
|
19039
|
+
pageId: string("The page's stable content id."),
|
|
19040
|
+
route: string("The page's route.")
|
|
19041
|
+
},
|
|
19042
|
+
required: ["kind", "label", "pageId", "route"],
|
|
19043
|
+
type: "object"
|
|
19044
|
+
},
|
|
19045
|
+
{
|
|
19046
|
+
properties: {
|
|
19047
|
+
badge: { type: "string" },
|
|
19048
|
+
children: { items: ref("NavNode"), type: "array" },
|
|
19049
|
+
collapsed: { type: "boolean" },
|
|
19050
|
+
icon: { type: "string" },
|
|
19051
|
+
kind: { const: "group", type: "string" },
|
|
19052
|
+
label: { type: "string" },
|
|
19053
|
+
path: string("The group's route prefix (not necessarily a page)."),
|
|
19054
|
+
route: string("The group's index page route, when it has one.")
|
|
19055
|
+
},
|
|
19056
|
+
required: ["children", "kind", "label"],
|
|
19057
|
+
type: "object"
|
|
19058
|
+
}
|
|
19059
|
+
]
|
|
19060
|
+
},
|
|
19061
|
+
NavSelector: {
|
|
19062
|
+
description: "A top-level partition selector (products, versions, languages).",
|
|
19063
|
+
properties: {
|
|
19064
|
+
items: {
|
|
19065
|
+
items: {
|
|
19066
|
+
properties: {
|
|
19067
|
+
description: { type: "string" },
|
|
19068
|
+
label: { type: "string" },
|
|
19069
|
+
path: { type: "string" },
|
|
19070
|
+
tag: { type: "string" }
|
|
19071
|
+
},
|
|
19072
|
+
required: ["label", "path"],
|
|
19073
|
+
type: "object"
|
|
19074
|
+
},
|
|
19075
|
+
type: "array"
|
|
19076
|
+
},
|
|
19077
|
+
kind: { type: "string" },
|
|
19078
|
+
label: { type: "string" }
|
|
19079
|
+
},
|
|
19080
|
+
required: ["items", "kind", "label"],
|
|
19081
|
+
type: "object"
|
|
19082
|
+
},
|
|
19083
|
+
NavTab: {
|
|
19084
|
+
properties: {
|
|
19085
|
+
href: string("The clickable target when it differs from `path` (the section's first page)."),
|
|
19086
|
+
icon: { type: "string" },
|
|
19087
|
+
label: { type: "string" },
|
|
19088
|
+
path: string("The tab's section prefix.")
|
|
19089
|
+
},
|
|
19090
|
+
required: ["label", "path"],
|
|
19091
|
+
type: "object"
|
|
19092
|
+
},
|
|
19093
|
+
Navigation: {
|
|
19094
|
+
description: "The docs navigation model: header tabs, the sidebar tree, partition selectors, and pinned links.",
|
|
19095
|
+
properties: {
|
|
19096
|
+
actions: { items: ref("NavLink"), type: "array" },
|
|
19097
|
+
cta: { oneOf: [ref("NavLink"), { type: "null" }] },
|
|
19098
|
+
featured: { items: ref("NavLink"), type: "array" },
|
|
19099
|
+
repoUrl: { type: ["string", "null"] },
|
|
19100
|
+
root: string("The tree root's route (`/`, or a locale/version prefix)."),
|
|
19101
|
+
selectors: { items: ref("NavSelector"), type: "array" },
|
|
19102
|
+
sidebar: { items: ref("NavNode"), type: "array" },
|
|
19103
|
+
tabs: { items: ref("NavTab"), type: "array" }
|
|
19104
|
+
},
|
|
19105
|
+
required: ["featured", "selectors", "sidebar", "tabs"],
|
|
19106
|
+
type: "object"
|
|
19107
|
+
},
|
|
19108
|
+
Page: {
|
|
19109
|
+
allOf: [
|
|
19110
|
+
ref("PageSummary"),
|
|
19111
|
+
{
|
|
19112
|
+
properties: {
|
|
19113
|
+
markdown: string("The page as agent Markdown: frontmatter included, components downleveled to plain Markdown.")
|
|
19114
|
+
},
|
|
19115
|
+
required: ["markdown"],
|
|
19116
|
+
type: "object"
|
|
19117
|
+
}
|
|
19118
|
+
],
|
|
19119
|
+
description: "A page's index entry plus its full Markdown body."
|
|
19120
|
+
},
|
|
19121
|
+
PageSummary: {
|
|
19122
|
+
properties: {
|
|
19123
|
+
contentType: string("The page's content type (frontmatter `type`)."),
|
|
19124
|
+
description: { type: "string" },
|
|
19125
|
+
facets: facetsSchema,
|
|
19126
|
+
json: {
|
|
19127
|
+
description: "This page's JSON representation (the `getPage` operation).",
|
|
19128
|
+
format: "uri-reference",
|
|
19129
|
+
type: "string"
|
|
19130
|
+
},
|
|
19131
|
+
lastModified: {
|
|
19132
|
+
description: "ISO 8601 last-modified date, when known.",
|
|
19133
|
+
type: ["string", "null"]
|
|
19134
|
+
},
|
|
19135
|
+
locale: string("The page's locale code."),
|
|
19136
|
+
markdownUrl: {
|
|
19137
|
+
description: "The page's raw-Markdown mirror (the `getPageMarkdown` operation).",
|
|
19138
|
+
format: "uri-reference",
|
|
19139
|
+
type: "string"
|
|
19140
|
+
},
|
|
19141
|
+
route: string("The page's route (`/guides/install`); the key every other operation takes."),
|
|
19142
|
+
title: { type: "string" },
|
|
19143
|
+
url: {
|
|
19144
|
+
description: "Where the rendered page is served.",
|
|
19145
|
+
format: "uri-reference",
|
|
19146
|
+
type: "string"
|
|
19147
|
+
},
|
|
19148
|
+
version: versionProperty
|
|
19149
|
+
},
|
|
19150
|
+
required: [
|
|
19151
|
+
"contentType",
|
|
19152
|
+
"json",
|
|
19153
|
+
"lastModified",
|
|
19154
|
+
"locale",
|
|
19155
|
+
"markdownUrl",
|
|
19156
|
+
"route",
|
|
19157
|
+
"title",
|
|
19158
|
+
"url"
|
|
19159
|
+
],
|
|
19160
|
+
type: "object"
|
|
19161
|
+
},
|
|
19162
|
+
PagesIndex: {
|
|
19163
|
+
properties: {
|
|
19164
|
+
count: { type: "integer" },
|
|
19165
|
+
generator: string("The Blume version that built the site."),
|
|
19166
|
+
pages: { items: ref("PageSummary"), type: "array" },
|
|
19167
|
+
site: { type: ["string", "null"] }
|
|
19168
|
+
},
|
|
19169
|
+
required: ["count", "generator", "pages", "site"],
|
|
19170
|
+
type: "object"
|
|
19171
|
+
},
|
|
19172
|
+
Problem: {
|
|
19173
|
+
description: "RFC 9457 problem details, with a stable code and a resolution hint.",
|
|
19174
|
+
properties: {
|
|
19175
|
+
code: string("Stable error code (e.g. `PAGE_NOT_FOUND`)."),
|
|
19176
|
+
detail: string("Human-readable explanation of this occurrence."),
|
|
19177
|
+
instance: string("The request path the problem occurred on."),
|
|
19178
|
+
links: {
|
|
19179
|
+
description: "Recovery links, when there is somewhere useful to go.",
|
|
19180
|
+
items: {
|
|
19181
|
+
properties: { href: { type: "string" }, label: { type: "string" } },
|
|
19182
|
+
required: ["href", "label"],
|
|
19183
|
+
type: "object"
|
|
19184
|
+
},
|
|
19185
|
+
type: "array"
|
|
19186
|
+
},
|
|
19187
|
+
resolution: string("What to do next."),
|
|
19188
|
+
status: { type: "integer" },
|
|
19189
|
+
title: { type: "string" },
|
|
19190
|
+
type: string("Problem type URI; `about:blank` by default.")
|
|
19191
|
+
},
|
|
19192
|
+
required: ["code", "detail", "resolution", "status", "title", "type"],
|
|
19193
|
+
type: "object"
|
|
19194
|
+
},
|
|
19195
|
+
SearchHit: {
|
|
19196
|
+
properties: {
|
|
19197
|
+
contentType: { type: "string" },
|
|
19198
|
+
excerpt: string("The page description, else the start of its content."),
|
|
19199
|
+
facets: facetsSchema,
|
|
19200
|
+
route: string("The page's route; pass it to `getPage`."),
|
|
19201
|
+
title: { type: "string" },
|
|
19202
|
+
url: { format: "uri-reference", type: "string" },
|
|
19203
|
+
version: versionProperty
|
|
19204
|
+
},
|
|
19205
|
+
required: ["excerpt", "route", "title", "url"],
|
|
19206
|
+
type: "object"
|
|
19207
|
+
},
|
|
19208
|
+
SearchResponse: {
|
|
19209
|
+
properties: {
|
|
19210
|
+
count: { type: "integer" },
|
|
19211
|
+
query: { type: "string" },
|
|
19212
|
+
results: { items: ref("SearchHit"), type: "array" }
|
|
19213
|
+
},
|
|
19214
|
+
required: ["count", "query", "results"],
|
|
19215
|
+
type: "object"
|
|
19216
|
+
}
|
|
19217
|
+
};
|
|
19218
|
+
var ROUTE_PARAM = {
|
|
19219
|
+
description: "The page route without its leading slash (`guides/install`), or `index` for the home page. May contain slashes.",
|
|
19220
|
+
in: "path",
|
|
19221
|
+
name: "route",
|
|
19222
|
+
required: true,
|
|
19223
|
+
schema: { type: "string" }
|
|
19224
|
+
};
|
|
19225
|
+
var serverUrl = (input) => {
|
|
19226
|
+
if (input.site) {
|
|
19227
|
+
return input.base ? absoluteUrl(input.site, input.base) : siteRoot(input.site);
|
|
19228
|
+
}
|
|
19229
|
+
return input.base || "/";
|
|
19230
|
+
};
|
|
19231
|
+
var buildApiSpec = (input) => {
|
|
19232
|
+
const pathEntries = [
|
|
19233
|
+
[
|
|
19234
|
+
API_PAGES_PATH,
|
|
19235
|
+
{
|
|
19236
|
+
get: {
|
|
19237
|
+
description: "Every documentation page with its route, title, description, content type, locale, facets, and the URLs of its rendered, Markdown, and JSON forms. Unfiltered; on a versioned site every version is listed with its `version`. Use it to enumerate the docs or to find a page when search is too narrow.",
|
|
19238
|
+
operationId: "listPages",
|
|
19239
|
+
responses: {
|
|
19240
|
+
"200": jsonResponse("The page index.", "PagesIndex"),
|
|
19241
|
+
default: problemResponse("An error, as problem details.")
|
|
19242
|
+
},
|
|
19243
|
+
summary: "List every page",
|
|
19244
|
+
tags: ["Pages"]
|
|
19245
|
+
}
|
|
19246
|
+
}
|
|
19247
|
+
],
|
|
19248
|
+
[
|
|
19249
|
+
API_PAGE_PATH,
|
|
19250
|
+
{
|
|
19251
|
+
get: {
|
|
19252
|
+
description: "A single page as JSON: its index entry plus the page's agent Markdown (frontmatter included, components downleveled to plain Markdown). Take `route` from `listPages` or `searchDocs`.",
|
|
19253
|
+
operationId: "getPage",
|
|
19254
|
+
parameters: [ROUTE_PARAM],
|
|
19255
|
+
responses: {
|
|
19256
|
+
"200": jsonResponse("The page.", "Page"),
|
|
19257
|
+
"404": problemResponse("No page has that route."),
|
|
19258
|
+
default: problemResponse("An error, as problem details.")
|
|
19259
|
+
},
|
|
19260
|
+
summary: "Get a page as JSON",
|
|
19261
|
+
tags: ["Pages"]
|
|
19262
|
+
}
|
|
19263
|
+
}
|
|
19264
|
+
],
|
|
19265
|
+
[
|
|
19266
|
+
API_NAVIGATION_PATH,
|
|
19267
|
+
{
|
|
19268
|
+
get: {
|
|
19269
|
+
description: "The navigation tree (header tabs and the sidebar hierarchy) as readers see it, for the default locale and the current docs.",
|
|
19270
|
+
operationId: "getNavigation",
|
|
19271
|
+
responses: {
|
|
19272
|
+
"200": jsonResponse("The navigation tree.", "Navigation"),
|
|
19273
|
+
default: problemResponse("An error, as problem details.")
|
|
19274
|
+
},
|
|
19275
|
+
summary: "Get the navigation tree",
|
|
19276
|
+
tags: ["Navigation"]
|
|
19277
|
+
}
|
|
19278
|
+
}
|
|
19279
|
+
]
|
|
19280
|
+
];
|
|
19281
|
+
if (input.search) {
|
|
19282
|
+
pathEntries.push([
|
|
19283
|
+
API_SEARCH_PATH,
|
|
19284
|
+
{
|
|
19285
|
+
get: {
|
|
19286
|
+
description: "Full-text search across the documentation. Returns matching pages with their title, route, content type, and a short excerpt; narrow by content type, locale, version, or facet. Use it first to discover relevant pages, then `getPage` to read one in full.",
|
|
19287
|
+
operationId: "searchDocs",
|
|
19288
|
+
parameters: searchParameters,
|
|
19289
|
+
responses: {
|
|
19290
|
+
"200": jsonResponse("The matching pages, best first.", "SearchResponse"),
|
|
19291
|
+
"400": problemResponse("The query was missing or blank."),
|
|
19292
|
+
default: problemResponse("An error, as problem details.")
|
|
19293
|
+
},
|
|
19294
|
+
summary: "Search the docs",
|
|
19295
|
+
tags: ["Search"]
|
|
19296
|
+
}
|
|
19297
|
+
}
|
|
19298
|
+
]);
|
|
19299
|
+
}
|
|
19300
|
+
pathEntries.push([
|
|
19301
|
+
"/{route}.md",
|
|
19302
|
+
{
|
|
19303
|
+
get: {
|
|
19304
|
+
description: "A page's raw-Markdown mirror: append `.md` to any page URL. Components are downleveled to plain Markdown; `.mdx` serves the source as written. The same body the `getPage` operation carries in its `markdown` field.",
|
|
19305
|
+
operationId: "getPageMarkdown",
|
|
19306
|
+
parameters: [ROUTE_PARAM],
|
|
19307
|
+
responses: {
|
|
19308
|
+
"200": textResponse("The page as Markdown.", MARKDOWN_TYPE),
|
|
19309
|
+
"404": textResponse("No page has that route; the body lists where to look next.", MARKDOWN_TYPE)
|
|
19310
|
+
},
|
|
19311
|
+
summary: "Get a page as Markdown",
|
|
19312
|
+
tags: ["Markdown"]
|
|
19313
|
+
}
|
|
19314
|
+
}
|
|
19315
|
+
]);
|
|
19316
|
+
if (input.llmsTxt) {
|
|
19317
|
+
pathEntries.push([
|
|
19318
|
+
"/llms.txt",
|
|
19319
|
+
{
|
|
19320
|
+
get: {
|
|
19321
|
+
description: "The llms.txt index: the site's summary, when to use it, and every page with a one-line description, grouped by section.",
|
|
19322
|
+
operationId: "getLlmsTxt",
|
|
19323
|
+
responses: {
|
|
19324
|
+
"200": textResponse("The index.", TEXT_TYPE)
|
|
19325
|
+
},
|
|
19326
|
+
summary: "Get llms.txt",
|
|
19327
|
+
tags: ["Markdown"]
|
|
19328
|
+
}
|
|
19329
|
+
}
|
|
19330
|
+
], [
|
|
19331
|
+
"/llms-full.txt",
|
|
19332
|
+
{
|
|
19333
|
+
get: {
|
|
19334
|
+
description: "The full Markdown of every current-docs page in one file.",
|
|
19335
|
+
operationId: "getLlmsFullTxt",
|
|
19336
|
+
responses: {
|
|
19337
|
+
"200": textResponse("Every page's Markdown.", TEXT_TYPE)
|
|
19338
|
+
},
|
|
19339
|
+
summary: "Get llms-full.txt",
|
|
19340
|
+
tags: ["Markdown"]
|
|
19341
|
+
}
|
|
19342
|
+
}
|
|
19343
|
+
]);
|
|
19344
|
+
}
|
|
19345
|
+
if (input.agentReadability) {
|
|
19346
|
+
pathEntries.push([
|
|
19347
|
+
"/agent-readability.json",
|
|
19348
|
+
{
|
|
19349
|
+
get: {
|
|
19350
|
+
description: "A manifest indexing every agent-facing artifact the site publishes — this API, the Markdown mirrors, llms.txt, the MCP server, feeds, and the sitemap.",
|
|
19351
|
+
operationId: "getAgentReadability",
|
|
19352
|
+
responses: {
|
|
19353
|
+
"200": {
|
|
19354
|
+
content: { [JSON_TYPE]: { schema: { type: "object" } } },
|
|
19355
|
+
description: "The manifest."
|
|
19356
|
+
}
|
|
19357
|
+
},
|
|
19358
|
+
summary: "Get the agent-readability manifest",
|
|
19359
|
+
tags: ["Discovery"]
|
|
19360
|
+
}
|
|
19361
|
+
}
|
|
19362
|
+
]);
|
|
19363
|
+
}
|
|
19364
|
+
if (input.mcpRoute) {
|
|
19365
|
+
pathEntries.push([
|
|
19366
|
+
input.mcpRoute,
|
|
19367
|
+
{
|
|
19368
|
+
post: {
|
|
19369
|
+
description: "The Model Context Protocol server (Streamable HTTP, stateless, JSON responses). Tools: `search_docs`, `get_page`, `list_pages`, `get_navigation` — the same operations this API exposes — plus every page as a `text/markdown` resource. Discovery document at `/.well-known/mcp.json`.",
|
|
19370
|
+
operationId: "mcp",
|
|
19371
|
+
requestBody: {
|
|
19372
|
+
content: { [JSON_TYPE]: { schema: ref("JsonRpcRequest") } },
|
|
19373
|
+
required: true
|
|
19374
|
+
},
|
|
19375
|
+
responses: {
|
|
19376
|
+
"200": jsonResponse("The JSON-RPC response.", "JsonRpcResponse")
|
|
19377
|
+
},
|
|
19378
|
+
summary: "Call the MCP server",
|
|
19379
|
+
tags: ["MCP"]
|
|
19380
|
+
}
|
|
19381
|
+
}
|
|
19382
|
+
]);
|
|
19383
|
+
}
|
|
19384
|
+
const tags = [
|
|
19385
|
+
{ description: "Enumerate and read documentation pages.", name: "Pages" },
|
|
19386
|
+
...input.search ? [{ description: "Full-text search over the docs.", name: "Search" }] : [],
|
|
19387
|
+
{ description: "How the docs are organized.", name: "Navigation" },
|
|
19388
|
+
{ description: "Plain-text and Markdown surfaces.", name: "Markdown" },
|
|
19389
|
+
...input.agentReadability ? [{ description: "Agent discovery documents.", name: "Discovery" }] : [],
|
|
19390
|
+
...input.mcpRoute ? [{ description: "The Model Context Protocol endpoint.", name: "MCP" }] : []
|
|
19391
|
+
];
|
|
19392
|
+
const info = {
|
|
19393
|
+
description: [
|
|
19394
|
+
`Read-only JSON API over the ${input.name} documentation${input.description ? `: ${input.description}` : "."}`,
|
|
19395
|
+
"Every operation is public and needs no authentication. Errors are RFC 9457 problem details (`application/problem+json`) with a stable `code`, a `detail`, and a `resolution` hint."
|
|
19396
|
+
].join(`
|
|
19397
|
+
|
|
19398
|
+
`),
|
|
19399
|
+
title: `${input.name} API`,
|
|
19400
|
+
version: input.version,
|
|
19401
|
+
"x-generator": `blume@${input.version}`
|
|
19402
|
+
};
|
|
19403
|
+
const document = {
|
|
19404
|
+
components: { schemas },
|
|
19405
|
+
info,
|
|
19406
|
+
openapi: "3.1.0",
|
|
19407
|
+
paths: Object.fromEntries(pathEntries),
|
|
19408
|
+
security: [],
|
|
19409
|
+
servers: [{ description: input.name, url: serverUrl(input) }],
|
|
19410
|
+
tags
|
|
19411
|
+
};
|
|
19412
|
+
if (input.site) {
|
|
19413
|
+
document.externalDocs = {
|
|
19414
|
+
description: `${input.name} documentation`,
|
|
19415
|
+
url: absoluteUrl(input.site, withBasePath(input.base, "/"))
|
|
19416
|
+
};
|
|
19417
|
+
}
|
|
19418
|
+
return document;
|
|
19419
|
+
};
|
|
19420
|
+
|
|
18734
19421
|
// src/ai/ask-data.ts
|
|
18735
19422
|
var buildAskData = async (project) => {
|
|
18736
19423
|
const documents = await buildSearchDocuments(project, {
|
|
@@ -18854,7 +19541,7 @@ var MCP_TOOLS = [
|
|
|
18854
19541
|
];
|
|
18855
19542
|
|
|
18856
19543
|
// src/ai/mcp/discovery.ts
|
|
18857
|
-
var
|
|
19544
|
+
var serverUrl2 = (input) => {
|
|
18858
19545
|
const path = withBasePath(input.base, input.route);
|
|
18859
19546
|
return input.site ? absoluteUrl(input.site, path) : path;
|
|
18860
19547
|
};
|
|
@@ -18863,7 +19550,7 @@ var buildMcpDiscovery = (input) => ({
|
|
|
18863
19550
|
{
|
|
18864
19551
|
name: input.name,
|
|
18865
19552
|
transport: "streamable-http",
|
|
18866
|
-
url:
|
|
19553
|
+
url: serverUrl2(input)
|
|
18867
19554
|
}
|
|
18868
19555
|
]
|
|
18869
19556
|
});
|
|
@@ -18884,7 +19571,7 @@ var reverseDnsName = (input) => {
|
|
|
18884
19571
|
};
|
|
18885
19572
|
var HTTP_URL2 = /^https?:\/\//u;
|
|
18886
19573
|
var buildMcpServerCard = (input) => {
|
|
18887
|
-
const url =
|
|
19574
|
+
const url = serverUrl2(input);
|
|
18888
19575
|
const card2 = {
|
|
18889
19576
|
$schema: SERVER_CARD_SCHEMA,
|
|
18890
19577
|
capabilities: {
|
|
@@ -19534,52 +20221,52 @@ var specConfiguration = async (spec, root) => {
|
|
|
19534
20221
|
};
|
|
19535
20222
|
}
|
|
19536
20223
|
};
|
|
19537
|
-
var acceptScalarReference = (
|
|
19538
|
-
if (
|
|
20224
|
+
var acceptScalarReference = (ref2, seen, contentRoutes, warnings) => {
|
|
20225
|
+
if (ref2.renderer !== "scalar") {
|
|
19539
20226
|
return null;
|
|
19540
20227
|
}
|
|
19541
|
-
if (seen.has(
|
|
19542
|
-
warnings.push(`Two API reference sources resolve to ${
|
|
20228
|
+
if (seen.has(ref2.route)) {
|
|
20229
|
+
warnings.push(`Two API reference sources resolve to ${ref2.route}; keeping the first.`);
|
|
19543
20230
|
return null;
|
|
19544
20231
|
}
|
|
19545
|
-
if (contentRoutes.has(
|
|
19546
|
-
warnings.push(`API reference route ${
|
|
20232
|
+
if (contentRoutes.has(ref2.route)) {
|
|
20233
|
+
warnings.push(`API reference route ${ref2.route} collides with a content page; skipping the reference there.`);
|
|
19547
20234
|
return null;
|
|
19548
20235
|
}
|
|
19549
|
-
seen.add(
|
|
19550
|
-
return
|
|
20236
|
+
seen.add(ref2.route);
|
|
20237
|
+
return ref2;
|
|
19551
20238
|
};
|
|
19552
20239
|
var buildReferenceFiles = async (options) => {
|
|
19553
20240
|
const { config, root, contentRoutes } = options;
|
|
19554
20241
|
const warnings = [];
|
|
19555
20242
|
const seen = new Set;
|
|
19556
20243
|
const accepted = [];
|
|
19557
|
-
for (const
|
|
19558
|
-
const next = acceptScalarReference(
|
|
20244
|
+
for (const ref2 of resolveReferences(config)) {
|
|
20245
|
+
const next = acceptScalarReference(ref2, seen, contentRoutes, warnings);
|
|
19559
20246
|
if (next) {
|
|
19560
20247
|
accepted.push(next);
|
|
19561
20248
|
}
|
|
19562
20249
|
}
|
|
19563
|
-
const built = await Promise.all(accepted.map(async (
|
|
19564
|
-
ref,
|
|
19565
|
-
spec: await specConfiguration(
|
|
20250
|
+
const built = await Promise.all(accepted.map(async (ref2) => ({
|
|
20251
|
+
ref: ref2,
|
|
20252
|
+
spec: await specConfiguration(ref2.spec, root)
|
|
19566
20253
|
})));
|
|
19567
20254
|
const files = [];
|
|
19568
|
-
for (const { ref, spec } of built) {
|
|
20255
|
+
for (const { ref: ref2, spec } of built) {
|
|
19569
20256
|
if (spec.warning) {
|
|
19570
20257
|
warnings.push(spec.warning);
|
|
19571
20258
|
}
|
|
19572
|
-
const pagePath = referencePagePath(
|
|
20259
|
+
const pagePath = referencePagePath(ref2.route);
|
|
19573
20260
|
files.push({
|
|
19574
20261
|
content: scalarReferenceTemplate({
|
|
19575
20262
|
configuration: {
|
|
19576
20263
|
...spec.config,
|
|
19577
|
-
...themeConfiguration(config,
|
|
19578
|
-
...
|
|
20264
|
+
...themeConfiguration(config, ref2.theme),
|
|
20265
|
+
...ref2.scalar
|
|
19579
20266
|
},
|
|
19580
|
-
noindex:
|
|
19581
|
-
route:
|
|
19582
|
-
title:
|
|
20267
|
+
noindex: ref2.noindex,
|
|
20268
|
+
route: ref2.route,
|
|
20269
|
+
title: ref2.label
|
|
19583
20270
|
}),
|
|
19584
20271
|
pagePath
|
|
19585
20272
|
});
|
|
@@ -19601,7 +20288,6 @@ var resolveSearchPopular = (popular, basePath) => popular.map(({ href, icon, lab
|
|
|
19601
20288
|
|
|
19602
20289
|
// src/theme/code-block-padding.ts
|
|
19603
20290
|
var CODE_PADDING_BLOCK_REM = 1;
|
|
19604
|
-
var FLUSH_CODE_PADDING_TOP_REM = 2.5;
|
|
19605
20291
|
|
|
19606
20292
|
// src/theme/entry.ts
|
|
19607
20293
|
var DARK_VARIANT = `/* Dark mode is driven by data-theme on the <html> element. */
|
|
@@ -20177,27 +20863,19 @@ blume-tabs pre[data-language],
|
|
|
20177
20863
|
padding-top: ${CODE_PADDING_BLOCK_REM}rem;
|
|
20178
20864
|
}
|
|
20179
20865
|
|
|
20180
|
-
/*
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
20184
|
-
|
|
20185
|
-
|
|
20186
|
-
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20190
|
-
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
\`.astro-code\`, no language bar, but the injector gives it a button all
|
|
20194
|
-
the same — keyed on that injected button, which is exactly what separates
|
|
20195
|
-
it from the playground's pre. API panel blocks match, but their own
|
|
20196
|
-
!important padding wins and they hide the injected button. */
|
|
20197
|
-
[data-blume-code-copy] :is(blume-tabs, .not-prose) pre.astro-code,
|
|
20198
|
-
[data-blume-code-copy] .prose pre.astro-code:not([data-language]),
|
|
20199
|
-
[data-blume-code-copy] .prose pre:not([data-language]):has(> [data-blume-copy]) {
|
|
20200
|
-
padding-top: ${FLUSH_CODE_PADDING_TOP_REM}rem;
|
|
20866
|
+
/* Where no language bar holds the copy button — a flush block in tabs or a
|
|
20867
|
+
not-prose component, a bar-less block in prose — the docs layout still pins
|
|
20868
|
+
one over the block's top-right corner, so a first line long enough to reach
|
|
20869
|
+
it would end underneath. A code switcher (CodeGroup, ts2js) hosts the button
|
|
20870
|
+
in its tab strip instead and needs nothing here; for every block that keeps
|
|
20871
|
+
an overlay button, give the scrolling code element enough end padding that
|
|
20872
|
+
the line's tail clears the button at the end of its scroll. Keyed on the
|
|
20873
|
+
injected button itself (which the playground's client-created response pre
|
|
20874
|
+
never gets), and \`.prose\`-scoped to outrank the base \`:where(pre code)\`
|
|
20875
|
+
inset. Not a vertical inset — that reserved a strip above every one-line
|
|
20876
|
+
command and read as a rendering bug. */
|
|
20877
|
+
.prose :is(blume-tabs pre, .not-prose pre, pre:not([data-language])):not(.twoslash, .twoslash pre, blume-panel-tabs *):has(> [data-blume-copy]) > code {
|
|
20878
|
+
padding-inline-end: 3.5rem;
|
|
20201
20879
|
}
|
|
20202
20880
|
|
|
20203
20881
|
blume-tabs pre[data-language]::before,
|
|
@@ -21311,6 +21989,7 @@ var buildRuntimeData = (project) => {
|
|
|
21311
21989
|
description: config.description,
|
|
21312
21990
|
discovery: {
|
|
21313
21991
|
agentReadability: config.seo.agentReadability,
|
|
21992
|
+
api: config.ai.api,
|
|
21314
21993
|
llmsTxt: config.ai.llmsTxt.enabled,
|
|
21315
21994
|
sitemap: config.seo.sitemap && Boolean(config.deployment.site)
|
|
21316
21995
|
},
|
|
@@ -21439,12 +22118,18 @@ var planMcp = (project, srcDir, userPages) => {
|
|
|
21439
22118
|
enabled: true
|
|
21440
22119
|
};
|
|
21441
22120
|
};
|
|
21442
|
-
var
|
|
21443
|
-
if (!
|
|
21444
|
-
return;
|
|
22121
|
+
var publishAgentData = async (project, plans, modules) => {
|
|
22122
|
+
if (!(plans.mcp.enabled || plans.api.enabled)) {
|
|
22123
|
+
return null;
|
|
21445
22124
|
}
|
|
21446
22125
|
const data = await buildMcpData(project);
|
|
21447
22126
|
modules.set("blume:mcp-data", JSON.stringify(data));
|
|
22127
|
+
return data;
|
|
22128
|
+
};
|
|
22129
|
+
var writeMcpFiles = async (plan, write, data) => {
|
|
22130
|
+
if (!(plan.enabled && data)) {
|
|
22131
|
+
return;
|
|
22132
|
+
}
|
|
21448
22133
|
const discoveryInput = {
|
|
21449
22134
|
base: data.base,
|
|
21450
22135
|
name: data.name,
|
|
@@ -21458,6 +22143,52 @@ var writeMcpFiles = async (project, plan, write, modules) => {
|
|
|
21458
22143
|
write(join31(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
|
|
21459
22144
|
]);
|
|
21460
22145
|
};
|
|
22146
|
+
var ownsApiRest = (page) => page.pattern.startsWith("/api/[");
|
|
22147
|
+
var contentUnderApi = (page) => page.route === "/api" || page.route.startsWith("/api/");
|
|
22148
|
+
var planApi = (project, srcDir, userPages) => {
|
|
22149
|
+
const { config, context } = project;
|
|
22150
|
+
const server = config.deployment.output === "server";
|
|
22151
|
+
return {
|
|
22152
|
+
catchAll: server && !userPages.some(ownsApiRest) && !project.graph.pages.some(contentUnderApi),
|
|
22153
|
+
enabled: config.ai.api,
|
|
22154
|
+
server,
|
|
22155
|
+
spec: !routeIsTaken(userPages, project.graph.pages, OPENAPI_PATH) && !existsSync17(join31(context.root, "public", "openapi.json")),
|
|
22156
|
+
srcDir
|
|
22157
|
+
};
|
|
22158
|
+
};
|
|
22159
|
+
var writeApiFiles = async (project, plan, write, data, mcp) => {
|
|
22160
|
+
if (!(plan.enabled && data)) {
|
|
22161
|
+
return;
|
|
22162
|
+
}
|
|
22163
|
+
const { config } = project;
|
|
22164
|
+
const mcpRoute = mcp.enabled ? mcp.route : null;
|
|
22165
|
+
const apiDir = join31(plan.srcDir, "pages", "api");
|
|
22166
|
+
const writes = [
|
|
22167
|
+
write(join31(apiDir, "docs", "pages.json.ts"), apiPagesIndexTemplate()),
|
|
22168
|
+
write(join31(apiDir, "docs", "pages", "[...route].json.ts"), apiPageTemplate()),
|
|
22169
|
+
write(join31(apiDir, "docs", "navigation.json.ts"), apiNavigationTemplate())
|
|
22170
|
+
];
|
|
22171
|
+
if (plan.server) {
|
|
22172
|
+
writes.push(write(join31(apiDir, "docs", "search.ts"), apiSearchTemplate()));
|
|
22173
|
+
}
|
|
22174
|
+
if (plan.catchAll) {
|
|
22175
|
+
writes.push(write(join31(apiDir, "[...path].ts"), apiNotFoundTemplate({ base: data.base, site: data.site })));
|
|
22176
|
+
}
|
|
22177
|
+
if (plan.spec) {
|
|
22178
|
+
writes.push(write(join31(plan.srcDir, "pages", "openapi.json.ts"), staticJsonEndpointTemplate(buildApiSpec({
|
|
22179
|
+
agentReadability: config.seo.agentReadability,
|
|
22180
|
+
base: data.base,
|
|
22181
|
+
description: config.description,
|
|
22182
|
+
llmsTxt: config.ai.llmsTxt.enabled,
|
|
22183
|
+
mcpRoute,
|
|
22184
|
+
name: config.title,
|
|
22185
|
+
search: plan.server,
|
|
22186
|
+
site: data.site,
|
|
22187
|
+
version: data.version
|
|
22188
|
+
}))));
|
|
22189
|
+
}
|
|
22190
|
+
await Promise.all(writes);
|
|
22191
|
+
};
|
|
21461
22192
|
var planPlaygroundProxy = (config, srcDir) => ({
|
|
21462
22193
|
enabled: needsPlaygroundProxy(config),
|
|
21463
22194
|
entrypoint: join31(srcDir, "blume-openapi", "api-proxy.ts"),
|
|
@@ -21514,7 +22245,8 @@ var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
|
|
|
21514
22245
|
}
|
|
21515
22246
|
await Promise.all([
|
|
21516
22247
|
write(join31(srcDir, "pages", "404.astro"), notFoundPageTemplate()),
|
|
21517
|
-
write(join31(srcDir, "pages", "404.md.ts"), notFoundMarkdownTemplate())
|
|
22248
|
+
write(join31(srcDir, "pages", "404.md.ts"), notFoundMarkdownTemplate()),
|
|
22249
|
+
write(join31(srcDir, "pages", "404.json.ts"), notFoundJsonTemplate())
|
|
21518
22250
|
]);
|
|
21519
22251
|
};
|
|
21520
22252
|
var diagnosticWarning = (diagnostic) => diagnostic.suggestion ? `${diagnostic.message} ${diagnostic.suggestion}` : diagnostic.message;
|
|
@@ -21604,6 +22336,8 @@ var generateRuntime = async (project) => {
|
|
|
21604
22336
|
const changelogIndex = hasGeneratedChangelog(project, pages);
|
|
21605
22337
|
const mcp = planMcp(project, srcDir, pages);
|
|
21606
22338
|
pages.push(...mcp.discoveryPages);
|
|
22339
|
+
const api = planApi(project, srcDir, pages);
|
|
22340
|
+
const agentData = await publishAgentData(project, { api, mcp }, modules);
|
|
21607
22341
|
const openApiSource2 = project.sources.find(isOpenApiSource);
|
|
21608
22342
|
const openApiData = openApiSource2 ? openApiSource2.openApiData() : {};
|
|
21609
22343
|
const playgroundProxy = planPlaygroundProxy(config, srcDir);
|
|
@@ -21676,7 +22410,8 @@ var generateRuntime = async (project) => {
|
|
|
21676
22410
|
Promise.all(slotPlan.wrappers.map((wrapper) => write(join31(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
|
|
21677
22411
|
Promise.all(exampleDiscovery.examples.map((example) => write(join31(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
|
|
21678
22412
|
writeAskFiles(project, srcDir, write, modules),
|
|
21679
|
-
writeMcpFiles(
|
|
22413
|
+
writeMcpFiles(mcp, write, agentData),
|
|
22414
|
+
writeApiFiles(project, api, write, agentData, mcp),
|
|
21680
22415
|
playgroundProxy.enabled ? write(playgroundProxy.entrypoint, playgroundProxyTemplate(proxyOrigins)) : Promise.resolve(false)
|
|
21681
22416
|
]);
|
|
21682
22417
|
if (config.seo.og.enabled) {
|
|
@@ -21988,8 +22723,11 @@ var emitVercelNegotiation = async (project, routePaths, root) => {
|
|
|
21988
22723
|
}
|
|
21989
22724
|
const rawMarkdown = await buildRawMarkdown(project);
|
|
21990
22725
|
const home = rawMarkdown["/"];
|
|
21991
|
-
const
|
|
21992
|
-
const injected = injectNegotiationRoutes(await readFile20(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config2, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined,
|
|
22726
|
+
const staticDir = join33(root, ".vercel", "output", "static");
|
|
22727
|
+
const injected = injectNegotiationRoutes(await readFile20(configPath, "utf-8"), routePaths, buildHomeLinkHeader(config2, routePaths), overrides, home ? markdownTokenCount(agentMarkdown(home)) : undefined, {
|
|
22728
|
+
json: existsSync19(join33(staticDir, "404.json")),
|
|
22729
|
+
markdown: existsSync19(join33(staticDir, "404.md"))
|
|
22730
|
+
});
|
|
21993
22731
|
if (injected === null) {
|
|
21994
22732
|
logger.warn("Could not wire Accept: text/markdown negotiation into .vercel/output/config.json — raw Markdown stays available at the .md URLs.");
|
|
21995
22733
|
return;
|
|
@@ -24489,6 +25227,9 @@ import {
|
|
|
24489
25227
|
McpError,
|
|
24490
25228
|
ReadResourceRequestSchema
|
|
24491
25229
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
25230
|
+
import { z as z8 } from "zod";
|
|
25231
|
+
|
|
25232
|
+
// src/ai/mcp/query.ts
|
|
24492
25233
|
import { z as z7 } from "zod";
|
|
24493
25234
|
|
|
24494
25235
|
// src/search/orama-index.ts
|
|
@@ -24632,13 +25373,10 @@ var queryOramaIndex = async (db, term, limit, filters) => {
|
|
|
24632
25373
|
return found.hits.map((hit) => hit.document);
|
|
24633
25374
|
};
|
|
24634
25375
|
|
|
24635
|
-
// src/ai/mcp/
|
|
25376
|
+
// src/ai/mcp/query.ts
|
|
24636
25377
|
var DEFAULT_SEARCH_LIMIT = 8;
|
|
24637
25378
|
var MAX_SEARCH_LIMIT = 20;
|
|
24638
25379
|
var EXCERPT_LENGTH = 200;
|
|
24639
|
-
var RESOURCE_MIME_TYPE = "text/markdown";
|
|
24640
|
-
var RESOURCE_NOT_FOUND = -32002;
|
|
24641
|
-
var LOCAL_RESOURCE_SCHEME = "blume:";
|
|
24642
25380
|
var contentTypesField = z7.preprocess((value) => {
|
|
24643
25381
|
const list2 = (Array.isArray(value) ? value : [value]).filter((entry) => typeof entry === "string");
|
|
24644
25382
|
return list2.length > 0 ? list2 : undefined;
|
|
@@ -24690,21 +25428,6 @@ var TOOL_INPUTS = {
|
|
|
24690
25428
|
version: versionField
|
|
24691
25429
|
})
|
|
24692
25430
|
};
|
|
24693
|
-
var inputSchemaFor = (schema) => {
|
|
24694
|
-
const {
|
|
24695
|
-
$schema: _dialect,
|
|
24696
|
-
additionalProperties: _closed,
|
|
24697
|
-
...rest
|
|
24698
|
-
} = z7.toJSONSchema(schema);
|
|
24699
|
-
return rest;
|
|
24700
|
-
};
|
|
24701
|
-
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
24702
|
-
annotations: tool.annotations,
|
|
24703
|
-
description: tool.description,
|
|
24704
|
-
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
24705
|
-
name: tool.name,
|
|
24706
|
-
title: tool.title
|
|
24707
|
-
}));
|
|
24708
25431
|
var matchesFacets = (facets, filters) => Object.entries(filters).every(([key, value]) => facets?.[key] === value);
|
|
24709
25432
|
var asVersionScope = (value, data) => {
|
|
24710
25433
|
if (!data.archivedVersions) {
|
|
@@ -24736,8 +25459,6 @@ var urlFor = (route, data) => {
|
|
|
24736
25459
|
const path = withBasePath(data.base, route);
|
|
24737
25460
|
return data.site ? absoluteUrl(data.site, path) : path;
|
|
24738
25461
|
};
|
|
24739
|
-
var resourceUri = (route, data) => data.site ? urlFor(route, data) : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;
|
|
24740
|
-
var resourceRoute = (uri, data) => normalizeRoute2(uri.startsWith(LOCAL_RESOURCE_SCHEME) ? uri.slice(LOCAL_RESOURCE_SCHEME.length) : uri, data);
|
|
24741
25462
|
var excerptFor = (doc) => {
|
|
24742
25463
|
if (doc.description) {
|
|
24743
25464
|
return doc.description;
|
|
@@ -24745,10 +25466,6 @@ var excerptFor = (doc) => {
|
|
|
24745
25466
|
const head = doc.content.slice(0, EXCERPT_LENGTH).trim();
|
|
24746
25467
|
return doc.content.length > EXCERPT_LENGTH ? `${head}…` : head;
|
|
24747
25468
|
};
|
|
24748
|
-
var text = (value, isError = false) => {
|
|
24749
|
-
const content = [{ text: value, type: "text" }];
|
|
24750
|
-
return isError ? { content, isError: true } : { content };
|
|
24751
|
-
};
|
|
24752
25469
|
var createIndexProvider = (documents, locale) => {
|
|
24753
25470
|
let dbPromise = null;
|
|
24754
25471
|
return function provideIndex() {
|
|
@@ -24756,6 +25473,90 @@ var createIndexProvider = (documents, locale) => {
|
|
|
24756
25473
|
return dbPromise;
|
|
24757
25474
|
};
|
|
24758
25475
|
};
|
|
25476
|
+
var searchDocs = async (data, index, input) => {
|
|
25477
|
+
const db = await index();
|
|
25478
|
+
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
25479
|
+
contentTypes: input.contentTypes,
|
|
25480
|
+
facets: input.filters,
|
|
25481
|
+
locale: input.locale,
|
|
25482
|
+
version: asVersionScope(input.version, data)
|
|
25483
|
+
});
|
|
25484
|
+
return hits.map((doc) => {
|
|
25485
|
+
const hit = {
|
|
25486
|
+
contentType: doc.contentType,
|
|
25487
|
+
excerpt: excerptFor(doc),
|
|
25488
|
+
facets: doc.facets,
|
|
25489
|
+
route: doc.route,
|
|
25490
|
+
title: doc.title,
|
|
25491
|
+
url: urlFor(doc.route, data)
|
|
25492
|
+
};
|
|
25493
|
+
if (data.archivedVersions) {
|
|
25494
|
+
hit.version = doc.version ?? "";
|
|
25495
|
+
}
|
|
25496
|
+
return hit;
|
|
25497
|
+
});
|
|
25498
|
+
};
|
|
25499
|
+
var getPageMarkdown = (data, route) => data.pages[route];
|
|
25500
|
+
var listPages = (data, input) => {
|
|
25501
|
+
const { contentTypes, filters, locale } = input;
|
|
25502
|
+
const versionScope = asVersionScope(input.version, data);
|
|
25503
|
+
return data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)) && (!locale || route.locale === locale) && (versionScope === undefined || route.version === versionScope)).map((route) => {
|
|
25504
|
+
const listing = {
|
|
25505
|
+
contentType: route.contentType,
|
|
25506
|
+
description: route.description,
|
|
25507
|
+
facets: route.facets,
|
|
25508
|
+
lastModified: route.lastModified,
|
|
25509
|
+
route: route.route,
|
|
25510
|
+
title: route.title,
|
|
25511
|
+
url: urlFor(route.route, data)
|
|
25512
|
+
};
|
|
25513
|
+
if (data.archivedVersions) {
|
|
25514
|
+
listing.version = route.version;
|
|
25515
|
+
}
|
|
25516
|
+
return listing;
|
|
25517
|
+
});
|
|
25518
|
+
};
|
|
25519
|
+
var getNavigation = (data, input) => {
|
|
25520
|
+
const { locale, version: versionId } = input;
|
|
25521
|
+
const unknownVersion = unknownVersionError(versionId, data);
|
|
25522
|
+
if (unknownVersion) {
|
|
25523
|
+
return { error: unknownVersion };
|
|
25524
|
+
}
|
|
25525
|
+
let { navigation } = data;
|
|
25526
|
+
const byLocale = versionId ? data.navigationByVersion?.[versionId] : undefined;
|
|
25527
|
+
if (byLocale) {
|
|
25528
|
+
navigation = (locale ? byLocale[locale] : undefined) ?? byLocale[data.defaultLocale ?? ""] ?? Object.values(byLocale)[0] ?? navigation;
|
|
25529
|
+
} else if (locale && data.navigationByLocale?.[locale]) {
|
|
25530
|
+
navigation = data.navigationByLocale[locale];
|
|
25531
|
+
}
|
|
25532
|
+
return { navigation };
|
|
25533
|
+
};
|
|
25534
|
+
|
|
25535
|
+
// src/ai/mcp/server.ts
|
|
25536
|
+
var RESOURCE_MIME_TYPE = "text/markdown";
|
|
25537
|
+
var RESOURCE_NOT_FOUND = -32002;
|
|
25538
|
+
var LOCAL_RESOURCE_SCHEME = "blume:";
|
|
25539
|
+
var inputSchemaFor = (schema) => {
|
|
25540
|
+
const {
|
|
25541
|
+
$schema: _dialect,
|
|
25542
|
+
additionalProperties: _closed,
|
|
25543
|
+
...rest
|
|
25544
|
+
} = z8.toJSONSchema(schema);
|
|
25545
|
+
return rest;
|
|
25546
|
+
};
|
|
25547
|
+
var TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
25548
|
+
annotations: tool.annotations,
|
|
25549
|
+
description: tool.description,
|
|
25550
|
+
inputSchema: inputSchemaFor(TOOL_INPUTS[tool.name]),
|
|
25551
|
+
name: tool.name,
|
|
25552
|
+
title: tool.title
|
|
25553
|
+
}));
|
|
25554
|
+
var resourceUri = (route, data) => data.site ? urlFor(route, data) : `${LOCAL_RESOURCE_SCHEME}${withBasePath(data.base, route)}`;
|
|
25555
|
+
var resourceRoute = (uri, data) => normalizeRoute2(uri.startsWith(LOCAL_RESOURCE_SCHEME) ? uri.slice(LOCAL_RESOURCE_SCHEME.length) : uri, data);
|
|
25556
|
+
var text = (value, isError = false) => {
|
|
25557
|
+
const content = [{ text: value, type: "text" }];
|
|
25558
|
+
return isError ? { content, isError: true } : { content };
|
|
25559
|
+
};
|
|
24759
25560
|
var buildServer = (data, index) => {
|
|
24760
25561
|
const capabilities = { resources: {}, tools: {} };
|
|
24761
25562
|
const serverOptions = data.instructions ? { capabilities, instructions: data.instructions } : { capabilities };
|
|
@@ -24779,7 +25580,7 @@ var buildServer = (data, index) => {
|
|
|
24779
25580
|
}));
|
|
24780
25581
|
server.setRequestHandler(ReadResourceRequestSchema, (request2) => {
|
|
24781
25582
|
const { uri } = request2.params;
|
|
24782
|
-
const markdown = data
|
|
25583
|
+
const markdown = getPageMarkdown(data, resourceRoute(uri, data));
|
|
24783
25584
|
if (markdown === undefined) {
|
|
24784
25585
|
throw new McpError(RESOURCE_NOT_FOUND, `No page found at "${uri}". Use resources/list or list_pages to find valid URIs.`);
|
|
24785
25586
|
}
|
|
@@ -24790,74 +25591,28 @@ var buildServer = (data, index) => {
|
|
|
24790
25591
|
server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
24791
25592
|
const { arguments: args = {}, name } = request2.params;
|
|
24792
25593
|
if (name === "search_docs") {
|
|
24793
|
-
const
|
|
24794
|
-
const db = await index();
|
|
24795
|
-
const hits = await queryOramaIndex(db, input.query, input.limit ?? DEFAULT_SEARCH_LIMIT, {
|
|
24796
|
-
contentTypes: input.contentTypes,
|
|
24797
|
-
facets: input.filters,
|
|
24798
|
-
locale: input.locale,
|
|
24799
|
-
version: asVersionScope(input.version, data)
|
|
24800
|
-
});
|
|
24801
|
-
const results = hits.map((doc) => {
|
|
24802
|
-
const hit = {
|
|
24803
|
-
contentType: doc.contentType,
|
|
24804
|
-
excerpt: excerptFor(doc),
|
|
24805
|
-
facets: doc.facets,
|
|
24806
|
-
route: doc.route,
|
|
24807
|
-
title: doc.title,
|
|
24808
|
-
url: urlFor(doc.route, data)
|
|
24809
|
-
};
|
|
24810
|
-
if (data.archivedVersions) {
|
|
24811
|
-
hit.version = doc.version ?? "";
|
|
24812
|
-
}
|
|
24813
|
-
return hit;
|
|
24814
|
-
});
|
|
25594
|
+
const results = await searchDocs(data, index, TOOL_INPUTS.search_docs.parse(args));
|
|
24815
25595
|
return text(JSON.stringify(results, null, 2));
|
|
24816
25596
|
}
|
|
24817
25597
|
if (name === "get_page") {
|
|
24818
25598
|
const input = TOOL_INPUTS.get_page.parse(args);
|
|
24819
25599
|
const key = normalizeRoute2(input.route, data);
|
|
24820
|
-
const markdown = data
|
|
25600
|
+
const markdown = getPageMarkdown(data, key);
|
|
24821
25601
|
if (markdown === undefined) {
|
|
24822
25602
|
return text(`No page found at "${key}". Use list_pages or search_docs to find valid routes.`, true);
|
|
24823
25603
|
}
|
|
24824
25604
|
return text(markdown);
|
|
24825
25605
|
}
|
|
24826
25606
|
if (name === "list_pages") {
|
|
24827
|
-
const
|
|
24828
|
-
|
|
24829
|
-
const versionScope = asVersionScope(input.version, data);
|
|
24830
|
-
const routes = data.routes.filter((route) => (!contentTypes || contentTypes.includes(route.contentType)) && (!filters || matchesFacets(route.facets, filters)) && (!locale || route.locale === locale) && (versionScope === undefined || route.version === versionScope));
|
|
24831
|
-
return text(JSON.stringify(routes.map((route) => {
|
|
24832
|
-
const listing = {
|
|
24833
|
-
contentType: route.contentType,
|
|
24834
|
-
description: route.description,
|
|
24835
|
-
facets: route.facets,
|
|
24836
|
-
lastModified: route.lastModified,
|
|
24837
|
-
route: route.route,
|
|
24838
|
-
title: route.title,
|
|
24839
|
-
url: urlFor(route.route, data)
|
|
24840
|
-
};
|
|
24841
|
-
if (data.archivedVersions) {
|
|
24842
|
-
listing.version = route.version;
|
|
24843
|
-
}
|
|
24844
|
-
return listing;
|
|
24845
|
-
}), null, 2));
|
|
25607
|
+
const listing = listPages(data, TOOL_INPUTS.list_pages.parse(args));
|
|
25608
|
+
return text(JSON.stringify(listing, null, 2));
|
|
24846
25609
|
}
|
|
24847
25610
|
if (name === "get_navigation") {
|
|
24848
|
-
const
|
|
24849
|
-
|
|
24850
|
-
|
|
24851
|
-
return text(unknownVersion, true);
|
|
25611
|
+
const result = getNavigation(data, TOOL_INPUTS.get_navigation.parse(args));
|
|
25612
|
+
if ("error" in result) {
|
|
25613
|
+
return text(result.error, true);
|
|
24852
25614
|
}
|
|
24853
|
-
|
|
24854
|
-
const byLocale = versionId ? data.navigationByVersion?.[versionId] : undefined;
|
|
24855
|
-
if (byLocale) {
|
|
24856
|
-
navigation = (locale ? byLocale[locale] : undefined) ?? byLocale[data.defaultLocale ?? ""] ?? Object.values(byLocale)[0] ?? navigation;
|
|
24857
|
-
} else if (locale && data.navigationByLocale?.[locale]) {
|
|
24858
|
-
navigation = data.navigationByLocale[locale];
|
|
24859
|
-
}
|
|
24860
|
-
return text(JSON.stringify(navigation, null, 2));
|
|
25615
|
+
return text(JSON.stringify(result.navigation, null, 2));
|
|
24861
25616
|
}
|
|
24862
25617
|
return text(`Unknown tool: ${name}`, true);
|
|
24863
25618
|
});
|
|
@@ -25009,11 +25764,11 @@ var translateAgentArgs = (kind, lastMessagePath) => kind === "claude" ? claudeAr
|
|
|
25009
25764
|
import { createHash as createHash5 } from "node:crypto";
|
|
25010
25765
|
import { readFile as readFile26 } from "node:fs/promises";
|
|
25011
25766
|
import { join as join45 } from "pathe";
|
|
25012
|
-
import { z as
|
|
25767
|
+
import { z as z9 } from "zod";
|
|
25013
25768
|
var LEDGER_FILE = "blume.translations.json";
|
|
25014
|
-
var ledgerSchema =
|
|
25015
|
-
files:
|
|
25016
|
-
version:
|
|
25769
|
+
var ledgerSchema = z9.object({
|
|
25770
|
+
files: z9.record(z9.string(), z9.record(z9.string(), z9.string())),
|
|
25771
|
+
version: z9.literal(1)
|
|
25017
25772
|
});
|
|
25018
25773
|
var emptyLedger = () => ({
|
|
25019
25774
|
files: {},
|
|
@@ -26150,18 +26905,18 @@ var checkPathLink = (resolved, fragment, page2, link, site, ctx, via = "") => {
|
|
|
26150
26905
|
};
|
|
26151
26906
|
};
|
|
26152
26907
|
var checkExternalLinks = async (refs) => {
|
|
26153
|
-
const results = await probeAll(refs.map((
|
|
26908
|
+
const results = await probeAll(refs.map((ref2) => ref2.url));
|
|
26154
26909
|
const diagnostics = [];
|
|
26155
|
-
for (const
|
|
26156
|
-
const result = results.get(
|
|
26910
|
+
for (const ref2 of refs) {
|
|
26911
|
+
const result = results.get(ref2.url);
|
|
26157
26912
|
const grade = result ? gradeExternal(result) : null;
|
|
26158
26913
|
if (grade) {
|
|
26159
26914
|
diagnostics.push({
|
|
26160
26915
|
code: "BLUME_DEAD_LINK",
|
|
26161
|
-
column:
|
|
26162
|
-
file:
|
|
26163
|
-
line:
|
|
26164
|
-
message: `External link ${
|
|
26916
|
+
column: ref2.column,
|
|
26917
|
+
file: ref2.file,
|
|
26918
|
+
line: ref2.line,
|
|
26919
|
+
message: `External link ${ref2.url} is unreachable (${grade.detail}).`,
|
|
26165
26920
|
severity: grade.severity
|
|
26166
26921
|
});
|
|
26167
26922
|
}
|
|
@@ -26215,7 +26970,7 @@ var validateLinks = async (graph, options) => {
|
|
|
26215
26970
|
let uncheckedAssets = 0;
|
|
26216
26971
|
for (const page2 of graph.pages) {
|
|
26217
26972
|
for (const link of page2.links) {
|
|
26218
|
-
const result = classifyLink(page2, link, ctx, (
|
|
26973
|
+
const result = classifyLink(page2, link, ctx, (ref2) => external.push(ref2));
|
|
26219
26974
|
if (result === "asset-unchecked") {
|
|
26220
26975
|
uncheckedAssets += 1;
|
|
26221
26976
|
} else if (result) {
|
|
@@ -26613,5 +27368,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
26613
27368
|
});
|
|
26614
27369
|
runMain(main, { rawArgs: normalizeHostArgs(process.argv.slice(2)) });
|
|
26615
27370
|
|
|
26616
|
-
//# debugId=
|
|
27371
|
+
//# debugId=91C1BFDA6463CA2664756E2164756E21
|
|
26617
27372
|
//# sourceMappingURL=index.js.map
|