---
summary: Landing page refactored into a modular directory, CSS-injection guard promoted to manifest-build time, Content-Security-Policy header added, per-request rendering memoized when publicUrl is set, and accessibility hygiene cleanups
breaking: false
---

# 0.6.9 — 2026-04-22

Landing page internals get a structural and hardening pass. The 1.7kLOC monolith is split into focused modules under `landing-page/`, the CSS-injection guard moves earlier in the pipeline so bad input fails fast, responses pick up a strict CSP, and the full page is precomputed once when `transport.publicUrl` is set instead of re-rendered per request.

## Added

- **Content-Security-Policy header** on `GET /` responses — `default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src 'self' data: https:; form-action 'none'; frame-ancestors 'none'; base-uri 'none'`. Reflects that the page is fully self-contained (single inline `<style>`, single inline `<script>`, logos as data URIs or `https://`). Present on both full and degraded responses.
- **Landing page render memoization** — when `manifest.transport.publicUrl` is set, both full and degraded page bodies are rendered once at handler-factory time and served from memory. Per-request rendering is still used when the origin must be derived from `new URL(c.req.url).origin`. Debug log records `cached: true/false` per request.
- **`--warn`, `--warn-text`, `--warn-bg`, `--warn-edge` design tokens** — reusable amber palette for warning surfaces. The pre-release chip now consumes these instead of hardcoded `#f59e0b`/`#b45309`/`#fbbf24` hex.

## Changed

- **Landing page split into a directory** — `src/mcp-server/transports/http/landing-page.ts` (~1.7kLOC monolith) is now `landing-page/` with `index.ts` (barrel), `handler.ts` (Hono factory + CSP + memoization), `render.ts` (top-level composition), `primitives.ts` (shared render helpers), `assets/{styles,copy-script}.ts`, and `sections/{hero,connect,status-strip,tools,resources,prompts,extensions,footer,head}.ts`. Public API (`createLandingPageHandler`, `renderLandingPage`) unchanged; consumers importing from the old path won't see anything.
- **`isSafeCssColor` promoted from linter to `@/core/serverManifest`** — now exported and called by `buildServerManifest` to throw `configurationError` when `landing.theme.accent` fails the CSS-safety check. The landing renderer keeps a silent fallback to the default indigo as a second line of defense for callers that construct manifests directly (tests, programmatic use). Lint rule continues to flag unsafe accents at startup for the same diagnostic in consumer logs.
- **Status strip `role="status"` removed** — the strip is static after initial render, not a live region; `role="status"` would cause screen readers to announce it on page load as if it were a change. The long-form auth phrase remains on `aria-label` for gated-ness readout.
- **Connect tabs' partial ARIA tab pattern removed** — `role="tablist"`, `role="tab"`, and `role="tabpanel"` are stripped from the radio-input-driven tab widget. The radios already communicate mutually-exclusive selection to assistive tech, and a partial ARIA pattern (no `aria-selected`, no `aria-controls` wiring) is worse than none.
