blume 0.0.0 → 0.1.1
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/bin/blume.mjs +18 -0
- package/dist/cli/index.js +12696 -0
- package/dist/cli/index.js.map +145 -0
- package/dist/types/core/bridge.d.ts +24 -0
- package/dist/types/core/config.d.ts +35 -0
- package/dist/types/core/data.d.ts +129 -0
- package/dist/types/core/define-components.d.ts +27 -0
- package/dist/types/core/define-meta.d.ts +16 -0
- package/dist/types/core/deployment-env.d.ts +9 -0
- package/dist/types/core/diagnostics.d.ts +17 -0
- package/dist/types/core/i18n-ui.d.ts +500 -0
- package/dist/types/core/load-module.d.ts +7 -0
- package/dist/types/core/package-root.d.ts +17 -0
- package/dist/types/core/project.d.ts +9 -0
- package/dist/types/core/schema.d.ts +3451 -0
- package/dist/types/core/sources/types.d.ts +107 -0
- package/dist/types/core/types.d.ts +245 -0
- package/dist/types/core/ui-packs/ar.d.ts +3 -0
- package/dist/types/core/ui-packs/bg.d.ts +3 -0
- package/dist/types/core/ui-packs/bn.d.ts +3 -0
- package/dist/types/core/ui-packs/ca.d.ts +3 -0
- package/dist/types/core/ui-packs/cs.d.ts +3 -0
- package/dist/types/core/ui-packs/da.d.ts +3 -0
- package/dist/types/core/ui-packs/de.d.ts +3 -0
- package/dist/types/core/ui-packs/el.d.ts +3 -0
- package/dist/types/core/ui-packs/es.d.ts +3 -0
- package/dist/types/core/ui-packs/fa.d.ts +3 -0
- package/dist/types/core/ui-packs/fi.d.ts +3 -0
- package/dist/types/core/ui-packs/fr.d.ts +3 -0
- package/dist/types/core/ui-packs/he.d.ts +3 -0
- package/dist/types/core/ui-packs/hi.d.ts +3 -0
- package/dist/types/core/ui-packs/hr.d.ts +3 -0
- package/dist/types/core/ui-packs/hu.d.ts +3 -0
- package/dist/types/core/ui-packs/id.d.ts +3 -0
- package/dist/types/core/ui-packs/index.d.ts +13 -0
- package/dist/types/core/ui-packs/it.d.ts +3 -0
- package/dist/types/core/ui-packs/ja.d.ts +3 -0
- package/dist/types/core/ui-packs/ko.d.ts +3 -0
- package/dist/types/core/ui-packs/nl.d.ts +3 -0
- package/dist/types/core/ui-packs/no.d.ts +3 -0
- package/dist/types/core/ui-packs/pl.d.ts +3 -0
- package/dist/types/core/ui-packs/pt-br.d.ts +3 -0
- package/dist/types/core/ui-packs/pt.d.ts +3 -0
- package/dist/types/core/ui-packs/ro.d.ts +3 -0
- package/dist/types/core/ui-packs/ru.d.ts +3 -0
- package/dist/types/core/ui-packs/sk.d.ts +3 -0
- package/dist/types/core/ui-packs/sr.d.ts +3 -0
- package/dist/types/core/ui-packs/sv.d.ts +3 -0
- package/dist/types/core/ui-packs/th.d.ts +3 -0
- package/dist/types/core/ui-packs/tr.d.ts +3 -0
- package/dist/types/core/ui-packs/uk.d.ts +3 -0
- package/dist/types/core/ui-packs/vi.d.ts +3 -0
- package/dist/types/core/ui-packs/zh-tw.d.ts +3 -0
- package/dist/types/core/ui-packs/zh.d.ts +3 -0
- package/dist/types/core/version.d.ts +8 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/migrate/mintlify/config.d.ts +2 -0
- package/dist/types/migrate/mintlify/i18n.d.ts +7 -0
- package/dist/types/theme/fonts.d.ts +163 -0
- package/docs/01-quickstart.mdx +99 -0
- package/docs/02-deployment.mdx +129 -0
- package/docs/advanced/api-reference.mdx +114 -0
- package/docs/advanced/blog.mdx +121 -0
- package/docs/advanced/changelog.mdx +113 -0
- package/docs/advanced/custom-pages.mdx +268 -0
- package/docs/advanced/meta.ts +7 -0
- package/docs/changelog/v0-1-0.mdx +12 -0
- package/docs/changelog/v0-2-0.mdx +16 -0
- package/docs/configuration/ai.mdx +228 -0
- package/docs/configuration/analytics.mdx +98 -0
- package/docs/configuration/customization.mdx +91 -0
- package/docs/configuration/export.mdx +70 -0
- package/docs/configuration/index.mdx +290 -0
- package/docs/configuration/meta.ts +15 -0
- package/docs/configuration/search.mdx +172 -0
- package/docs/configuration/seo.mdx +196 -0
- package/docs/configuration/theming.mdx +178 -0
- package/docs/content/components.mdx +651 -0
- package/docs/content/i18n.mdx +205 -0
- package/docs/content/index.mdx +161 -0
- package/docs/content/islands.mdx +94 -0
- package/docs/content/meta.mdx +119 -0
- package/docs/content/meta.ts +15 -0
- package/docs/content/navigation.mdx +168 -0
- package/docs/content/sources.mdx +216 -0
- package/docs/content/syntax.mdx +445 -0
- package/docs/index.mdx +112 -0
- package/docs/reference/cli.mdx +43 -0
- package/docs/reference/frontmatter.mdx +74 -0
- package/docs/reference/meta.ts +7 -0
- package/package.json +150 -6
- package/src/ai/ask.ts +93 -0
- package/src/ai/llms.ts +64 -0
- package/src/ai/markdown.ts +31 -0
- package/src/ai/mcp/data.ts +74 -0
- package/src/ai/mcp/discovery.ts +49 -0
- package/src/ai/mcp/server.ts +225 -0
- package/src/ai/mcp/tools.ts +47 -0
- package/src/assets/icon.png +0 -0
- package/src/astro/examples.ts +104 -0
- package/src/astro/generate.ts +1006 -0
- package/src/astro/index.ts +4 -0
- package/src/astro/integration.ts +74 -0
- package/src/astro/islands.ts +131 -0
- package/src/astro/markdown-negotiation.ts +68 -0
- package/src/astro/pages.ts +87 -0
- package/src/astro/templates.ts +1326 -0
- package/src/cli/commands/add.ts +81 -0
- package/src/cli/commands/build.ts +103 -0
- package/src/cli/commands/dev.ts +108 -0
- package/src/cli/commands/doctor.ts +74 -0
- package/src/cli/commands/eject.ts +57 -0
- package/src/cli/commands/init.ts +98 -0
- package/src/cli/commands/migrate.ts +39 -0
- package/src/cli/commands/preview.ts +39 -0
- package/src/cli/commands/sync.ts +52 -0
- package/src/cli/commands/validate.ts +61 -0
- package/src/cli/index.ts +35 -0
- package/src/cli/log.ts +37 -0
- package/src/cli/prepare.ts +80 -0
- package/src/components/Icon.astro +99 -0
- package/src/components/content/Accordion.astro +8 -0
- package/src/components/content/AccordionItem.astro +121 -0
- package/src/components/content/AutoTypeTable.astro +51 -0
- package/src/components/content/Badge.astro +124 -0
- package/src/components/content/Callout.astro +73 -0
- package/src/components/content/Card.astro +104 -0
- package/src/components/content/CardGroup.astro +14 -0
- package/src/components/content/CodeBlock.astro +28 -0
- package/src/components/content/CodeGroup.astro +13 -0
- package/src/components/content/Color.astro +15 -0
- package/src/components/content/ColorItem.astro +87 -0
- package/src/components/content/ColorRow.astro +10 -0
- package/src/components/content/Column.astro +6 -0
- package/src/components/content/Columns.astro +9 -0
- package/src/components/content/Component.astro +65 -0
- package/src/components/content/Diff.astro +44 -0
- package/src/components/content/Expandable.astro +11 -0
- package/src/components/content/FileTree.astro +8 -0
- package/src/components/content/Frame.astro +70 -0
- package/src/components/content/GithubInfo.astro +110 -0
- package/src/components/content/Math.astro +24 -0
- package/src/components/content/Panel.astro +20 -0
- package/src/components/content/Prompt.astro +129 -0
- package/src/components/content/Step.astro +34 -0
- package/src/components/content/Steps.astro +20 -0
- package/src/components/content/Tab.astro +46 -0
- package/src/components/content/Tabs.astro +273 -0
- package/src/components/content/Tile.astro +42 -0
- package/src/components/content/Tooltip.astro +68 -0
- package/src/components/content/Tree.astro +300 -0
- package/src/components/content/TreeFile.astro +15 -0
- package/src/components/content/TreeFolder.astro +62 -0
- package/src/components/content/TypeTable.astro +106 -0
- package/src/components/content/Update.astro +66 -0
- package/src/components/content/Visibility.astro +12 -0
- package/src/components/content/Warning.astro +9 -0
- package/src/components/content/auto-type-table.ts +141 -0
- package/src/components/content/diff.ts +95 -0
- package/src/components/content/github-info.ts +79 -0
- package/src/components/content/mermaid-element.ts +68 -0
- package/src/components/github-mark.ts +9 -0
- package/src/components/index.ts +14 -0
- package/src/components/islands/AskAI.astro +12 -0
- package/src/components/islands/ask-ai.tsx +156 -0
- package/src/components/layout/Analytics.astro +63 -0
- package/src/components/layout/Banner.astro +50 -0
- package/src/components/layout/Breadcrumbs.astro +31 -0
- package/src/components/layout/Favicon.astro +22 -0
- package/src/components/layout/Fonts.astro +14 -0
- package/src/components/layout/Header.astro +188 -0
- package/src/components/layout/LanguageSwitcher.astro +56 -0
- package/src/components/layout/NavTree.astro +462 -0
- package/src/components/layout/PageActions.astro +438 -0
- package/src/components/layout/PageFeedback.astro +58 -0
- package/src/components/layout/PageLayout.astro +173 -0
- package/src/components/layout/Pagination.astro +56 -0
- package/src/components/layout/ReferenceLayout.astro +107 -0
- package/src/components/layout/RootLayout.astro +537 -0
- package/src/components/layout/Search.astro +608 -0
- package/src/components/layout/TableOfContents.astro +68 -0
- package/src/components/layout/analytics-client.ts +38 -0
- package/src/components/layout/head-scripts.ts +19 -0
- package/src/components/layout/nav-utils.ts +87 -0
- package/src/components/layout/overrides.ts +32 -0
- package/src/components/layout/search/algolia.ts +43 -0
- package/src/components/layout/search/endpoint.ts +22 -0
- package/src/components/layout/search/flexsearch.ts +52 -0
- package/src/components/layout/search/orama-cloud.ts +41 -0
- package/src/components/layout/search/orama.ts +26 -0
- package/src/components/layout/search/pagefind.ts +43 -0
- package/src/components/layout/search/types.ts +163 -0
- package/src/components/layout/search/typesense.ts +60 -0
- package/src/components/layout/toc-element.ts +108 -0
- package/src/core/bridge.ts +92 -0
- package/src/core/config.ts +112 -0
- package/src/core/content.ts +50 -0
- package/src/core/data.ts +128 -0
- package/src/core/define-components.ts +34 -0
- package/src/core/define-meta.ts +20 -0
- package/src/core/deployment-env.ts +73 -0
- package/src/core/diagnostics.ts +104 -0
- package/src/core/frontmatter.ts +43 -0
- package/src/core/graph.ts +128 -0
- package/src/core/i18n-ui.ts +171 -0
- package/src/core/i18n.ts +169 -0
- package/src/core/last-modified.ts +88 -0
- package/src/core/links.ts +352 -0
- package/src/core/load-module.ts +15 -0
- package/src/core/manifest.ts +126 -0
- package/src/core/meta.ts +97 -0
- package/src/core/navigation.ts +392 -0
- package/src/core/package-root.ts +37 -0
- package/src/core/project-graph.ts +153 -0
- package/src/core/project.ts +56 -0
- package/src/core/schema.ts +1057 -0
- package/src/core/server-features.ts +23 -0
- package/src/core/sources/assets.ts +77 -0
- package/src/core/sources/cache.ts +122 -0
- package/src/core/sources/filesystem.ts +99 -0
- package/src/core/sources/mdx-remote.ts +215 -0
- package/src/core/sources/mintlify.ts +161 -0
- package/src/core/sources/normalize.ts +243 -0
- package/src/core/sources/notion.ts +440 -0
- package/src/core/sources/portable-text.ts +143 -0
- package/src/core/sources/read.ts +36 -0
- package/src/core/sources/resolve.ts +158 -0
- package/src/core/sources/sanity.ts +218 -0
- package/src/core/sources/types.ts +105 -0
- package/src/core/tsconfig-aliases.ts +201 -0
- package/src/core/types.ts +261 -0
- package/src/core/ui-packs/ar.ts +47 -0
- package/src/core/ui-packs/bg.ts +47 -0
- package/src/core/ui-packs/bn.ts +47 -0
- package/src/core/ui-packs/ca.ts +47 -0
- package/src/core/ui-packs/cs.ts +47 -0
- package/src/core/ui-packs/da.ts +47 -0
- package/src/core/ui-packs/de.ts +47 -0
- package/src/core/ui-packs/el.ts +47 -0
- package/src/core/ui-packs/es.ts +47 -0
- package/src/core/ui-packs/fa.ts +47 -0
- package/src/core/ui-packs/fi.ts +47 -0
- package/src/core/ui-packs/fr.ts +47 -0
- package/src/core/ui-packs/he.ts +47 -0
- package/src/core/ui-packs/hi.ts +47 -0
- package/src/core/ui-packs/hr.ts +47 -0
- package/src/core/ui-packs/hu.ts +47 -0
- package/src/core/ui-packs/id.ts +47 -0
- package/src/core/ui-packs/index.ts +87 -0
- package/src/core/ui-packs/it.ts +47 -0
- package/src/core/ui-packs/ja.ts +47 -0
- package/src/core/ui-packs/ko.ts +47 -0
- package/src/core/ui-packs/nl.ts +47 -0
- package/src/core/ui-packs/no.ts +47 -0
- package/src/core/ui-packs/pl.ts +47 -0
- package/src/core/ui-packs/pt-br.ts +47 -0
- package/src/core/ui-packs/pt.ts +47 -0
- package/src/core/ui-packs/ro.ts +47 -0
- package/src/core/ui-packs/ru.ts +47 -0
- package/src/core/ui-packs/sk.ts +47 -0
- package/src/core/ui-packs/sr.ts +47 -0
- package/src/core/ui-packs/sv.ts +47 -0
- package/src/core/ui-packs/th.ts +47 -0
- package/src/core/ui-packs/tr.ts +47 -0
- package/src/core/ui-packs/uk.ts +47 -0
- package/src/core/ui-packs/vi.ts +47 -0
- package/src/core/ui-packs/zh-tw.ts +47 -0
- package/src/core/ui-packs/zh.ts +47 -0
- package/src/core/version.ts +23 -0
- package/src/deploy/robots.ts +20 -0
- package/src/deploy/rss.ts +128 -0
- package/src/deploy/sitemap.ts +28 -0
- package/src/index.ts +39 -0
- package/src/markdown/code-title.ts +71 -0
- package/src/markdown/directives.ts +83 -0
- package/src/markdown/heading-anchors.ts +137 -0
- package/src/markdown/index.ts +228 -0
- package/src/markdown/inline-code.ts +108 -0
- package/src/markdown/language-icon.ts +172 -0
- package/src/markdown/math.ts +32 -0
- package/src/markdown/mdast.ts +48 -0
- package/src/markdown/mermaid.ts +37 -0
- package/src/markdown/package-commands.ts +159 -0
- package/src/markdown/package-install.ts +40 -0
- package/src/migrate/fumadocs/config.ts +155 -0
- package/src/migrate/fumadocs/content.ts +365 -0
- package/src/migrate/fumadocs/frontmatter.ts +18 -0
- package/src/migrate/fumadocs/groups.ts +230 -0
- package/src/migrate/fumadocs/index.ts +337 -0
- package/src/migrate/fumadocs/meta.ts +244 -0
- package/src/migrate/migrate.ts +53 -0
- package/src/migrate/mintlify/config.ts +1040 -0
- package/src/migrate/mintlify/content.ts +98 -0
- package/src/migrate/mintlify/frontmatter.ts +126 -0
- package/src/migrate/mintlify/i18n.ts +51 -0
- package/src/migrate/mintlify/icons.ts +128 -0
- package/src/migrate/mintlify/index.ts +266 -0
- package/src/migrate/mintlify/snippets.ts +306 -0
- package/src/migrate/mintlify/transform.ts +80 -0
- package/src/migrate/nextra/content.ts +46 -0
- package/src/migrate/nextra/frontmatter.ts +40 -0
- package/src/migrate/nextra/index.ts +374 -0
- package/src/migrate/nextra/meta.ts +266 -0
- package/src/migrate/shared.ts +720 -0
- package/src/migrate/starlight/config.ts +459 -0
- package/src/migrate/starlight/content.ts +78 -0
- package/src/migrate/starlight/frontmatter.ts +111 -0
- package/src/migrate/starlight/i18n.ts +54 -0
- package/src/migrate/starlight/index.ts +131 -0
- package/src/og/card.ts +92 -0
- package/src/og/index.ts +2 -0
- package/src/openapi/scalar.ts +246 -0
- package/src/registry/eject.ts +310 -0
- package/src/registry/registry.ts +100 -0
- package/src/registry/rewrite-imports.ts +39 -0
- package/src/runtime/index.ts +14 -0
- package/src/search/build.ts +23 -0
- package/src/search/documents.ts +164 -0
- package/src/search/orama-index.ts +66 -0
- package/src/search/providers.ts +91 -0
- package/src/search/sync/algolia.ts +30 -0
- package/src/search/sync/index.ts +50 -0
- package/src/search/sync/orama-cloud.ts +40 -0
- package/src/search/sync/typesense.ts +65 -0
- package/src/seo/jsonld.ts +113 -0
- package/src/theme/entry.ts +637 -0
- package/src/theme/fonts.ts +198 -0
- package/src/theme/icons.ts +184 -0
- package/src/theme/palette.ts +143 -0
- package/src/theme/twoslash.ts +81 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
|
|
8
|
+
import { buildOramaIndex, queryOramaIndex } from "../../search/orama-index.ts";
|
|
9
|
+
import type { OramaDoc } from "../../search/orama-index.ts";
|
|
10
|
+
import type { McpData } from "./data.ts";
|
|
11
|
+
import { MCP_TOOLS } from "./tools.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The low-level SDK `Server` is used (rather than the high-level `McpServer`)
|
|
15
|
+
* because the latter's `registerTool` is generic over the caller's Zod version;
|
|
16
|
+
* Blume pins Zod 3 while the SDK resolves Zod 4, so their types don't unify.
|
|
17
|
+
* Hand-written JSON Schema and the SDK's own request schemas avoid that entirely.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Default and maximum number of hits returned by `search_docs`. */
|
|
21
|
+
const DEFAULT_SEARCH_LIMIT = 8;
|
|
22
|
+
const MAX_SEARCH_LIMIT = 20;
|
|
23
|
+
/** Excerpt length when a page has no description. */
|
|
24
|
+
const EXCERPT_LENGTH = 200;
|
|
25
|
+
|
|
26
|
+
const CORS_HEADERS: Record<string, string> = {
|
|
27
|
+
"Access-Control-Allow-Headers":
|
|
28
|
+
"Content-Type, Mcp-Session-Id, Mcp-Protocol-Version",
|
|
29
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
30
|
+
"Access-Control-Allow-Origin": "*",
|
|
31
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** JSON Schema for each tool's input, keyed by tool name. */
|
|
35
|
+
const INPUT_SCHEMAS: Record<string, Record<string, unknown>> = {
|
|
36
|
+
get_navigation: { properties: {}, type: "object" },
|
|
37
|
+
get_page: {
|
|
38
|
+
properties: {
|
|
39
|
+
route: {
|
|
40
|
+
description: "The page route, e.g. `/guides/install`.",
|
|
41
|
+
type: "string",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ["route"],
|
|
45
|
+
type: "object",
|
|
46
|
+
},
|
|
47
|
+
list_pages: { properties: {}, type: "object" },
|
|
48
|
+
search_docs: {
|
|
49
|
+
properties: {
|
|
50
|
+
limit: {
|
|
51
|
+
description: `Maximum hits to return (default ${DEFAULT_SEARCH_LIMIT}).`,
|
|
52
|
+
maximum: MAX_SEARCH_LIMIT,
|
|
53
|
+
minimum: 1,
|
|
54
|
+
type: "integer",
|
|
55
|
+
},
|
|
56
|
+
query: { description: "The search query.", type: "string" },
|
|
57
|
+
},
|
|
58
|
+
required: ["query"],
|
|
59
|
+
type: "object",
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** The `tools/list` payload, derived from shared metadata + input schemas. */
|
|
64
|
+
const TOOL_DEFINITIONS = MCP_TOOLS.map((tool) => ({
|
|
65
|
+
annotations: tool.annotations,
|
|
66
|
+
description: tool.description,
|
|
67
|
+
inputSchema: INPUT_SCHEMAS[tool.name],
|
|
68
|
+
name: tool.name,
|
|
69
|
+
title: tool.title,
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
const asString = (value: unknown): string =>
|
|
73
|
+
typeof value === "string" ? value : "";
|
|
74
|
+
|
|
75
|
+
const asLimit = (value: unknown): number => {
|
|
76
|
+
const num = typeof value === "number" ? value : Number(value);
|
|
77
|
+
if (!Number.isFinite(num)) {
|
|
78
|
+
return DEFAULT_SEARCH_LIMIT;
|
|
79
|
+
}
|
|
80
|
+
return Math.min(Math.max(Math.trunc(num), 1), MAX_SEARCH_LIMIT);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Normalize a user-supplied route to a `pages` key (`/`, `/a/b`, no suffix). */
|
|
84
|
+
const normalizeRoute = (input: string): string => {
|
|
85
|
+
const noTrailing = input.trim().replace(/\/+$/u, "");
|
|
86
|
+
const noSuffix = noTrailing.replace(/\.mdx?$/u, "");
|
|
87
|
+
const withSlash = noSuffix.startsWith("/") ? noSuffix : `/${noSuffix}`;
|
|
88
|
+
return withSlash === "" ? "/" : withSlash;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Build the absolute (or root-relative) URL for a route. */
|
|
92
|
+
const urlFor = (route: string, site: string | null): string =>
|
|
93
|
+
site ? new URL(route, site).href : route;
|
|
94
|
+
|
|
95
|
+
const text = (value: string, isError = false) => ({
|
|
96
|
+
content: [{ text: value, type: "text" as const }],
|
|
97
|
+
...(isError ? { isError: true } : {}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/** Construct a fresh MCP server with Blume's read-only docs tools registered. */
|
|
101
|
+
const buildServer = (
|
|
102
|
+
data: McpData,
|
|
103
|
+
index: () => Promise<Awaited<ReturnType<typeof buildOramaIndex>>>
|
|
104
|
+
): Server => {
|
|
105
|
+
const server = new Server(
|
|
106
|
+
{ name: data.name, version: data.version },
|
|
107
|
+
{
|
|
108
|
+
capabilities: { tools: {} },
|
|
109
|
+
...(data.instructions ? { instructions: data.instructions } : {}),
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
114
|
+
tools: TOOL_DEFINITIONS,
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
118
|
+
const { arguments: args = {}, name } = request.params;
|
|
119
|
+
|
|
120
|
+
if (name === "search_docs") {
|
|
121
|
+
const db = await index();
|
|
122
|
+
const hits = await queryOramaIndex(
|
|
123
|
+
db,
|
|
124
|
+
asString(args.query),
|
|
125
|
+
asLimit(args.limit)
|
|
126
|
+
);
|
|
127
|
+
const results = hits.map((doc: OramaDoc) => ({
|
|
128
|
+
excerpt:
|
|
129
|
+
doc.description || `${doc.content.slice(0, EXCERPT_LENGTH)}…`.trim(),
|
|
130
|
+
title: doc.title,
|
|
131
|
+
url: urlFor(doc.route, data.site),
|
|
132
|
+
}));
|
|
133
|
+
return text(JSON.stringify(results, null, 2));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (name === "get_page") {
|
|
137
|
+
const key = normalizeRoute(asString(args.route));
|
|
138
|
+
const markdown = data.pages[key];
|
|
139
|
+
if (markdown === undefined) {
|
|
140
|
+
return text(
|
|
141
|
+
`No page found at "${key}". Use list_pages or search_docs to find valid routes.`,
|
|
142
|
+
true
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return text(markdown);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (name === "list_pages") {
|
|
149
|
+
return text(
|
|
150
|
+
JSON.stringify(
|
|
151
|
+
data.routes.map((route) => ({
|
|
152
|
+
contentType: route.contentType,
|
|
153
|
+
description: route.description,
|
|
154
|
+
lastModified: route.lastModified,
|
|
155
|
+
route: route.route,
|
|
156
|
+
title: route.title,
|
|
157
|
+
url: urlFor(route.route, data.site),
|
|
158
|
+
})),
|
|
159
|
+
null,
|
|
160
|
+
2
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (name === "get_navigation") {
|
|
166
|
+
return text(JSON.stringify(data.navigation, null, 2));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return text(`Unknown tool: ${name}`, true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return server;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Build a stateless Streamable-HTTP MCP request handler from a data snapshot.
|
|
177
|
+
*
|
|
178
|
+
* The Orama index is built once and reused; a fresh `Server` and transport are
|
|
179
|
+
* created per request (required by the SDK's stateless mode, which skips session
|
|
180
|
+
* tracking). `enableJsonResponse` makes each call a plain request/response — no
|
|
181
|
+
* SSE — which suits read-only docs tools and runs on any adapter (Node, Vercel,
|
|
182
|
+
* Netlify, Cloudflare). CORS is added so browser-based connectors (e.g.
|
|
183
|
+
* claude.ai) can reach the endpoint.
|
|
184
|
+
*/
|
|
185
|
+
export const createMcpFetchHandler = (
|
|
186
|
+
data: McpData
|
|
187
|
+
): ((request: Request) => Promise<Response>) => {
|
|
188
|
+
let dbPromise: Promise<Awaited<ReturnType<typeof buildOramaIndex>>> | null =
|
|
189
|
+
null;
|
|
190
|
+
const index = () => {
|
|
191
|
+
dbPromise ??= buildOramaIndex(data.documents);
|
|
192
|
+
return dbPromise;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return async (request: Request): Promise<Response> => {
|
|
196
|
+
if (request.method === "OPTIONS") {
|
|
197
|
+
return new Response(null, { headers: CORS_HEADERS, status: 204 });
|
|
198
|
+
}
|
|
199
|
+
if (request.method === "GET") {
|
|
200
|
+
// No server-initiated streams are needed for read-only tools.
|
|
201
|
+
return new Response("Method Not Allowed", {
|
|
202
|
+
headers: { ...CORS_HEADERS, Allow: "POST, OPTIONS" },
|
|
203
|
+
status: 405,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const server = buildServer(data, index);
|
|
208
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
209
|
+
enableJsonResponse: true,
|
|
210
|
+
sessionIdGenerator: undefined,
|
|
211
|
+
});
|
|
212
|
+
await server.connect(transport);
|
|
213
|
+
const response = await transport.handleRequest(request);
|
|
214
|
+
|
|
215
|
+
const headers = new Headers(response.headers);
|
|
216
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
217
|
+
headers.set(key, value);
|
|
218
|
+
}
|
|
219
|
+
return new Response(response.body, {
|
|
220
|
+
headers,
|
|
221
|
+
status: response.status,
|
|
222
|
+
statusText: response.statusText,
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static metadata for the MCP tools Blume exposes. Shared by the server (which
|
|
3
|
+
* pairs each entry with a Zod input schema and handler) and the `.well-known`
|
|
4
|
+
* discovery documents, so tool names and descriptions never drift between the
|
|
5
|
+
* live server and its advertised capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** A read-only, non-mutating tool hint set (every Blume tool is read-only). */
|
|
9
|
+
const READ_ONLY = { openWorldHint: false, readOnlyHint: true } as const;
|
|
10
|
+
|
|
11
|
+
export interface McpToolMeta {
|
|
12
|
+
annotations: { openWorldHint: boolean; readOnlyHint: boolean };
|
|
13
|
+
description: string;
|
|
14
|
+
name: string;
|
|
15
|
+
title: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MCP_TOOLS: McpToolMeta[] = [
|
|
19
|
+
{
|
|
20
|
+
annotations: READ_ONLY,
|
|
21
|
+
description:
|
|
22
|
+
"Full-text search across the documentation. Returns matching pages with their title, route, and a short excerpt. Use this first to discover relevant pages, then `get_page` to read one in full.",
|
|
23
|
+
name: "search_docs",
|
|
24
|
+
title: "Search documentation",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
annotations: READ_ONLY,
|
|
28
|
+
description:
|
|
29
|
+
"Fetch a single documentation page as its original Markdown source (frontmatter included). Pass a route from `search_docs` or `list_pages`, e.g. `/guides/install`.",
|
|
30
|
+
name: "get_page",
|
|
31
|
+
title: "Get page Markdown",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
annotations: READ_ONLY,
|
|
35
|
+
description:
|
|
36
|
+
"List every documentation page with its route, title, description, and content type. Useful for enumerating the docs or finding a page when search is too narrow.",
|
|
37
|
+
name: "list_pages",
|
|
38
|
+
title: "List pages",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
annotations: READ_ONLY,
|
|
42
|
+
description:
|
|
43
|
+
"Return the documentation navigation tree (header tabs and the sidebar hierarchy), reflecting how the docs are organized for readers.",
|
|
44
|
+
name: "get_navigation",
|
|
45
|
+
title: "Get navigation",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
Binary file
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { join, relative } from "pathe";
|
|
4
|
+
import { glob } from "tinyglobby";
|
|
5
|
+
|
|
6
|
+
import type { IslandClientMode } from "./islands.ts";
|
|
7
|
+
import { readClientMode } from "./islands.ts";
|
|
8
|
+
|
|
9
|
+
/** Framework an example is authored in, inferred from its extension. */
|
|
10
|
+
export type ExampleFramework = "astro" | "react" | "svelte" | "vue";
|
|
11
|
+
|
|
12
|
+
/** A discovered `examples/` file, ready to wrap (live) and show (source). */
|
|
13
|
+
export interface ExampleSpec {
|
|
14
|
+
/** Hydration directive for a framework example; `undefined` for `.astro`. */
|
|
15
|
+
client?: IslandClientMode;
|
|
16
|
+
/** Absolute path to the example source file. */
|
|
17
|
+
file: string;
|
|
18
|
+
/** Framework, or `astro` for a server-rendered (static) example. */
|
|
19
|
+
framework: ExampleFramework;
|
|
20
|
+
/** Shiki language for the source pane — the file extension. */
|
|
21
|
+
lang: string;
|
|
22
|
+
/** MDX-facing key: the path under `examples/`, sans extension, `/`-joined. */
|
|
23
|
+
path: string;
|
|
24
|
+
/** Raw source text shown in the code tab. */
|
|
25
|
+
source: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ExampleDiscovery {
|
|
29
|
+
examples: ExampleSpec[];
|
|
30
|
+
warnings: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Example extensions mapped to the framework that renders them. */
|
|
34
|
+
const FRAMEWORK_BY_EXT: Record<string, ExampleFramework> = {
|
|
35
|
+
astro: "astro",
|
|
36
|
+
jsx: "react",
|
|
37
|
+
svelte: "svelte",
|
|
38
|
+
tsx: "react",
|
|
39
|
+
vue: "vue",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Captures the extension so we can strip it from the path key and pick the
|
|
43
|
+
// framework. Kept in sync with the glob below.
|
|
44
|
+
const EXAMPLE_FILE = /\.(?<ext>astro|jsx|svelte|tsx|vue)$/u;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Discover preview examples under `<root>/examples`. Every `.astro`/`.tsx`/
|
|
48
|
+
* `.jsx`/`.vue`/`.svelte` file becomes addressable by `<Component path="...">`,
|
|
49
|
+
* where the path is the file's location under `examples/` without its extension
|
|
50
|
+
* (e.g. `forms/login.tsx` → `forms/login`). Discovery is path-based (a glob),
|
|
51
|
+
* so no example code is executed. Framework examples carry a hydration mode
|
|
52
|
+
* (default `client:visible`, overridable via `export const client`); `.astro`
|
|
53
|
+
* examples render statically with no client directive.
|
|
54
|
+
*/
|
|
55
|
+
export const discoverExamples = async (
|
|
56
|
+
root: string
|
|
57
|
+
): Promise<ExampleDiscovery> => {
|
|
58
|
+
const dir = join(root, "examples");
|
|
59
|
+
const matches = await glob(["**/*.{astro,jsx,svelte,tsx,vue}"], {
|
|
60
|
+
absolute: true,
|
|
61
|
+
cwd: dir,
|
|
62
|
+
onlyFiles: true,
|
|
63
|
+
});
|
|
64
|
+
const files = matches.toSorted();
|
|
65
|
+
const sources = await Promise.all(
|
|
66
|
+
files.map((file) => readFile(file, "utf-8"))
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const examples: ExampleSpec[] = [];
|
|
70
|
+
const warnings: string[] = [];
|
|
71
|
+
const seen = new Map<string, string>();
|
|
72
|
+
|
|
73
|
+
for (const [index, file] of files.entries()) {
|
|
74
|
+
const ext = file.match(EXAMPLE_FILE)?.groups?.ext;
|
|
75
|
+
const framework = ext ? FRAMEWORK_BY_EXT[ext] : undefined;
|
|
76
|
+
if (!(ext && framework)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Strip the trailing `.<ext>` to form the `<Component path>` key.
|
|
80
|
+
const path = relative(dir, file).slice(0, -(ext.length + 1));
|
|
81
|
+
const existing = seen.get(path);
|
|
82
|
+
if (existing) {
|
|
83
|
+
warnings.push(
|
|
84
|
+
`Two examples both resolve to "${path}" ("${existing}" and "${file}"); ignoring the second. Give them distinct paths.`
|
|
85
|
+
);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
seen.set(path, file);
|
|
89
|
+
const source = sources[index] ?? "";
|
|
90
|
+
examples.push({
|
|
91
|
+
client:
|
|
92
|
+
framework === "astro"
|
|
93
|
+
? undefined
|
|
94
|
+
: readClientMode(source, file, warnings),
|
|
95
|
+
file,
|
|
96
|
+
framework,
|
|
97
|
+
lang: ext,
|
|
98
|
+
path,
|
|
99
|
+
source,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { examples, warnings };
|
|
104
|
+
};
|