Docs: unblock local docs build without AI Search#1107
Conversation
|
There was a problem hiding this comment.
Pull request overview
This PR updates the docs app to build and run local/built Worker previews without requiring a live Cloudflare AI Search binding, while preserving the production AI Search-backed MCP behavior when configured.
Changes:
- Add a local-only Wrangler config (
wrangler.local.jsonc) that omits theai_searchbinding for local/built preview use. - Add an MCP
/mcptool fallback in the docs Worker whenAI_SEARCHis not configured. - Customize 404 handling by disabling Starlight’s injected 404 route and adding a prerendered
src/pages/404.astro, and adjust the Cloudflare adapter config to avoid remote binding dependency during builds. - Clarify the docs README for the local built-worker preview flow vs. production-binding previews.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| docs/wrangler.local.jsonc | Adds a Wrangler config for local preview that doesn’t require AI Search bindings. |
| docs/src/worker.ts | Adds a safe MCP tool fallback when AI_SEARCH is missing; uses a local aiSearch reference for search calls. |
| docs/src/pages/404.astro | Introduces a custom prerendered Starlight 404 page. |
| docs/README.md | Documents the local-only Wrangler preview flow and notes MCP behavior without AI Search. |
| docs/astro.config.mjs | Disables Starlight’s 404 route and configures the Cloudflare adapter to avoid remote bindings during builds. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| server.registerTool( | ||
| "search_docs", | ||
| { | ||
| title: "Search EmDash documentation", | ||
| description: | ||
| "Search the EmDash CMS documentation. This endpoint is not configured in this environment.", | ||
| inputSchema: { |
There was a problem hiding this comment.
This is a maintainability issue rather than a functional bug, but I agree that the duplicated tool metadata could drift over time. I can factor the shared search_docs tool definition into a single shared spec and only branch the handler behavior based on AI_SEARCH availability.
There was a problem hiding this comment.
I refactored this so the search_docs tool definition is shared in one place and only the handler implementation varies based on AI_SEARCH availability.
That removes duplicate schema/metadata and prevents drift between the fallback and AI-enabled paths.
Validation I ran after the fix:
- prettier --check docs/src/worker.ts
- pnpm --dir docs build
|
/review |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Scoped, low-risk docs build fix. The intent is clear and the changes hang together: skip AI Search at build/prerender time and degrade gracefully at runtime when the binding is absent. Both disable404Route and Astro.locals.t("404.text") are valid Starlight APIs, and remoteBindings/prerenderEnvironment are valid @astrojs/cloudflare adapter options. The Copilot-recommended dedupe of searchDocsTool has already been applied.
A few things worth thinking about before merge:
- The adapter changes in
astro.config.mjsaffect production builds too, not just local. That's almost certainly fine here (docs are fully static; nothing prerendered touchesenv.AI_SEARCH), but it is a real behavior change for the production deploy pipeline -- worth confirming the next CI deploy still produces what you expect. Env.AI_SEARCHis typed as non-optional inworker-configuration.d.ts, so theif (!aiSearch)guard works at runtime but doesn't actually narrow at the type level. Functionally fine; cosmetically a bit fuzzy.wrangler.local.jsoncduplicatescompatibility_date,compatibility_flags,name,main,assets, andobservabilityfromwrangler.jsonc. They will drift. Adding a brief comment that mirrors this restriction (or a README pointer) would help future-you.
No blockers.
| ], | ||
|
|
||
| adapter: cloudflare(), | ||
| adapter: cloudflare({ remoteBindings: false, prerenderEnvironment: "node" }), |
There was a problem hiding this comment.
These two options apply to every build (local, CI, and the production deploy), not just the local-only flow described in the README. That's almost certainly what you want -- the docs site is fully static and nothing prerendered touches env.AI_SEARCH -- but the PR description frames this as "avoid remote binding dependency during docs builds" which slightly understates the scope.
Worth verifying the next prod deploy still works as expected (especially that prerenderEnvironment: "node" doesn't regress any workerd-specific behavior the prior config relied on). If the goal was strictly local-only, the alternative would be conditional config via an env var, but that's probably overkill here.
There was a problem hiding this comment.
This does affect all docs builds, not just local preview, and that is intentional here. The docs site is fully static, and nothing in prerender depends on workerd-only behavior or env.AI_SEARCH, so using prerenderEnvironment: "node" and remoteBindings: false for the docs app is the desired behavior rather than a local-only toggle.
| version: "1.0.0", | ||
| }); | ||
| const aiSearch = env.AI_SEARCH; | ||
| if (!aiSearch) { |
There was a problem hiding this comment.
Env.AI_SEARCH is typed as AiSearchInstance (non-optional) in worker-configuration.d.ts, so from TypeScript's perspective this guard is dead code -- aiSearch is always truthy. At runtime, however, the binding really is undefined when running under wrangler.local.jsonc, so the check does work; it's just not type-narrowed.
If you want the type system to reflect reality, you could type-narrow with an explicit cast or local declaration, e.g.:
| if (!aiSearch) { | |
| const aiSearch = (env as { AI_SEARCH?: typeof env.AI_SEARCH }).AI_SEARCH; | |
| if (!aiSearch) { |
Not a blocker -- behavior is correct as-is.
There was a problem hiding this comment.
I updated this so the runtime fallback matches the type story more closely by reading AI_SEARCH through an explicit optional cast before the guard.
That keeps the fallback behavior the same at runtime while making it clearer that the binding may be absent in the local-only Wrangler config.
| @@ -0,0 +1,14 @@ | |||
| { | |||
| "$schema": "node_modules/wrangler/config-schema.json", | |||
| "compatibility_date": "2026-03-01", | |||
There was a problem hiding this comment.
This config duplicates compatibility_date, compatibility_flags, name, main, assets, and observability from wrangler.jsonc. If any of those change in the primary config (especially compatibility_date and compatibility_flags), this file will silently drift and local previews will diverge from production behavior.
A short comment at the top noting "keep in sync with wrangler.jsonc" -- or a checklist line in docs/README.md -- would save a future debugging session. Not blocking.
There was a problem hiding this comment.
Agreed. I added a short sync note at the top of wrangler.local.jsonc so it’s clearer that the shared fields should stay aligned with wrangler.jsonc.
| editUrl: false, | ||
| pagefind: false, | ||
| hero: { | ||
| tagline: Astro.locals.t("404.text"), |
There was a problem hiding this comment.
Confirmed Astro.locals.t("404.text") is the documented Starlight i18n helper (see @astrojs/starlight/locals.d.ts) and 404.text is one of the built-in translation keys, so this works even though the site doesn't configure custom i18n. Just flagging for posterity.
|
Review posted successfully. Summary of the review:
|
What does this PR do?
Fixes the docs app so local/built Worker preview and docs builds do not require a live Cloudflare AI Search binding.
Closes #
AI_SEARCHis not configured.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
pnpm --dir docs buildprettier --checkon changed docs files