Skip to content

Commit 7e3f0cf

Browse files
Docs: unblock local docs build without AI Search (#1107)
* Docs: unblock local docs build without AI Search * Docs: deduplicate MCP tool definition * style: format * Docs: clarify local AI Search fallback config * style: format --------- Co-authored-by: emdashbot[bot] <emdashbot[bot]@users.noreply.github.com>
1 parent a7e8202 commit 7e3f0cf

5 files changed

Lines changed: 111 additions & 50 deletions

File tree

docs/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,28 @@ Documentation site for EmDash, built with [Starlight](https://starlight.astro.bu
88
pnpm dev
99
```
1010

11+
If you're running in a remote or API-token-only environment that cannot access the
12+
Cloudflare AI Search instance, keep using `pnpm dev` for local content work and
13+
use the local-only Wrangler config for built-worker preview checks instead:
14+
15+
```bash
16+
pnpm build
17+
pnpm exec wrangler dev --config wrangler.local.jsonc
18+
```
19+
20+
The `/mcp` endpoint still works, but it returns a helpful message until a
21+
Cloudflare AI Search binding is configured.
22+
1123
## Build
1224

1325
```bash
1426
pnpm build
1527
```
28+
29+
For remote docs preview/deploy checks that require production bindings, run the
30+
main config as usual:
31+
32+
```bash
33+
pnpm exec wrangler dev
34+
pnpm exec wrangler deploy
35+
```

docs/astro.config.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default defineConfig({
1010
starlight({
1111
title: "EmDash",
1212
tagline: "The Astro-native CMS",
13+
disable404Route: true,
1314
components: {
1415
SkipLink: "./src/components/SkipLink.astro",
1516
},
@@ -237,5 +238,5 @@ export default defineConfig({
237238
}),
238239
],
239240

240-
adapter: cloudflare(),
241+
adapter: cloudflare({ remoteBindings: false, prerenderEnvironment: "node" }),
241242
});

docs/src/pages/404.astro

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
3+
4+
export const prerender = true;
5+
6+
const frontmatter = {
7+
title: "404",
8+
template: "splash",
9+
editUrl: false,
10+
pagefind: false,
11+
hero: {
12+
tagline: Astro.locals.t("404.text"),
13+
actions: [],
14+
},
15+
};
16+
---
17+
18+
<StarlightPage frontmatter={frontmatter} />

docs/src/worker.ts

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,66 +8,73 @@ import { z } from "zod";
88
* underlying transport asserts that the server is not already connected, so we
99
* cannot reuse a single server instance across requests.
1010
*/
11+
const searchDocsTool = {
12+
title: "Search EmDash documentation",
13+
description:
14+
"Search the EmDash CMS documentation. Returns relevant chunks with source URLs and similarity scores.",
15+
inputSchema: {
16+
query: z.string().min(1).max(1000).describe("Natural-language query against the EmDash docs."),
17+
max_results: z
18+
.number()
19+
.int()
20+
.min(1)
21+
.max(20)
22+
.optional()
23+
.describe("Maximum number of chunks to return. Defaults to 8."),
24+
},
25+
};
26+
1127
function buildMcpServer(env: Env): McpServer {
1228
const server = new McpServer({
1329
name: "emdash-docs",
1430
version: "1.0.0",
1531
});
32+
const aiSearch = (env as { AI_SEARCH?: Env["AI_SEARCH"] }).AI_SEARCH;
33+
if (!aiSearch) {
34+
server.registerTool("search_docs", searchDocsTool, async () => ({
35+
content: [
36+
{
37+
type: "text",
38+
text: "Docs search is unavailable in this environment. Configure Cloudflare AI Search in the Workers binding to enable this tool.",
39+
},
40+
],
41+
}));
1642

17-
server.registerTool(
18-
"search_docs",
19-
{
20-
title: "Search EmDash documentation",
21-
description:
22-
"Search the EmDash CMS documentation. Returns relevant chunks with source URLs and similarity scores.",
23-
inputSchema: {
24-
query: z
25-
.string()
26-
.min(1)
27-
.max(1000)
28-
.describe("Natural-language query against the EmDash docs."),
29-
max_results: z
30-
.number()
31-
.int()
32-
.min(1)
33-
.max(20)
34-
.optional()
35-
.describe("Maximum number of chunks to return. Defaults to 8."),
36-
},
37-
},
38-
async ({ query, max_results }) => {
39-
const limit = max_results ?? 8;
43+
return server;
44+
}
4045

41-
const results = await env.AI_SEARCH.search({
42-
messages: [{ role: "user", content: query }],
43-
ai_search_options: {
44-
retrieval: { max_num_results: limit },
45-
},
46-
});
46+
server.registerTool("search_docs", searchDocsTool, async ({ query, max_results }) => {
47+
const limit = max_results ?? 8;
4748

48-
if (!results.chunks.length) {
49-
return {
50-
content: [
51-
{
52-
type: "text",
53-
text: "No matching docs found.",
54-
},
55-
],
56-
};
57-
}
49+
const results = await aiSearch.search({
50+
messages: [{ role: "user", content: query }],
51+
ai_search_options: {
52+
retrieval: { max_num_results: limit },
53+
},
54+
});
5855

56+
if (!results.chunks.length) {
5957
return {
60-
content: results.chunks.map((chunk) => {
61-
const source = chunk.item.key;
62-
const score = typeof chunk.score === "number" ? chunk.score.toFixed(3) : "n/a";
63-
return {
64-
type: "text" as const,
65-
text: `<result source="${source}" score="${score}">\n${chunk.text}\n</result>`,
66-
};
67-
}),
58+
content: [
59+
{
60+
type: "text",
61+
text: "No matching docs found.",
62+
},
63+
],
6864
};
69-
},
70-
);
65+
}
66+
67+
return {
68+
content: results.chunks.map((chunk) => {
69+
const source = chunk.item.key;
70+
const score = typeof chunk.score === "number" ? chunk.score.toFixed(3) : "n/a";
71+
return {
72+
type: "text" as const,
73+
text: `<result source="${source}" score="${score}">\n${chunk.text}\n</result>`,
74+
};
75+
}),
76+
};
77+
});
7178

7279
return server;
7380
}

docs/wrangler.local.jsonc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
// Keep shared fields in sync with wrangler.jsonc.
3+
"$schema": "node_modules/wrangler/config-schema.json",
4+
"compatibility_date": "2026-03-01",
5+
"compatibility_flags": ["global_fetch_strictly_public", "nodejs_compat"],
6+
"name": "docs",
7+
"main": "./src/worker.ts",
8+
"assets": {
9+
"directory": "./dist",
10+
"binding": "ASSETS",
11+
},
12+
"observability": {
13+
"enabled": true,
14+
},
15+
}

0 commit comments

Comments
 (0)