---
summary: linter exempts z.literal union variants from describe-on-fields; landing connect snippets resist Cloudflare email rewriting and accept operator overrides; maintenance skill surfaces new/changed skills
breaking: false
---

# 0.7.4 — 2026-04-24

Three small enhancements queued from issues [#67](https://github.com/cyanheads/mcp-ts-core/issues/67), [#65](https://github.com/cyanheads/mcp-ts-core/issues/65), and [#59](https://github.com/cyanheads/mcp-ts-core/issues/59). The linter walker now matches the structurally-correct form-client tolerance pattern (`z.union([z.literal(''), z.string().regex(...).describe(...)])`) instead of forcing it into the handler-guard alternative; the landing-page connect card defends against Cloudflare's edge email scanner by default and accepts per-tab operator overrides; the maintenance skill names new framework skills explicitly so they don't get synced silently.

## Added

- **`landing.connectSnippets` operator override ([#65](https://github.com/cyanheads/mcp-ts-core/issues/65))** — new optional `Partial<Record<'stdio'|'http'|'claude'|'curl', string>>` field on `LandingConfig` that replaces the derived snippet for any tab the operator sets. Unset tabs continue to derive from `envExample` + server identity as before. Use to express client shapes the derivation can't produce: Docker / in-process launchers, custom `env` merging, alternate `command`/`args`. Linter rules: `landing-connect-snippets-type` (error), `landing-connect-snippets-value` (error), `landing-connect-snippets-key` (warning, unknown tab id), `landing-connect-snippets-empty` (warning, empty string falls back to derived). Single source of truth via new exported `CONNECT_SNIPPET_TABS` const tuple — type, normalizer, and linter rule all reference it.
- **Cloudflare Email Obfuscation defense in landing connect card ([#65](https://github.com/cyanheads/mcp-ts-core/issues/65))** — every connect-panel `<pre><code>` now wraps content in `<!--email_off-->…<!--/email_off-->`. CF's edge scanner honors the directive and stops rewriting email-shaped placeholders (e.g. `you@example.com`), which previously broke the Copy button's output for any consumer deployed behind a CF Tunnel. Two HTML comments per panel; ignored everywhere else; not surfaced through `textContent` so copy behavior is unchanged.

## Changed

- **`describe-on-fields` exempts `z.literal` union variants ([#67](https://github.com/cyanheads/mcp-ts-core/issues/67))** — the linter walker now skips union options whose unwrapped `_zod.def.type === 'literal'`. Structural-marker literals (e.g. the `z.literal('')` form-client blank-tolerance sentinel in `z.union([z.literal(''), z.string().regex(...).describe(...)])`) no longer require a redundant inner describe — the outer union describe carries the semantic load and a literal-variant describe would ship to JSON Schema as clutter. `z.discriminatedUnion` is unaffected: its options are objects, not bare literals. Wrapper-aware via shared `getCoreDefType` helper, so `z.union([z.literal('').optional(), z.string()])` is also handled correctly. `CLAUDE.md` / `AGENTS.md` / `templates/CLAUDE.md` / `templates/AGENTS.md` Form-client safety guidance updated to name the union+literal alternative for cases where schema-level regex/length matters enough to surface in JSON Schema.
- **`maintenance` skill surfaces new and materially-changed skills ([#59](https://github.com/cyanheads/mcp-ts-core/issues/59))** — bumped to v1.6. Step 4 scan table gets a row for "New or materially-changed skills" with explicit guidance to flag skills that arrived via Phase A sync but not auto-invoke them (some, like `security-pass`, are user-triggered). Step 8 summary gets a new "New/changed skills available" bullet so consumers don't lose track of capabilities that were synced silently. Resolves the silent-skill-sync drift observed during 0.6.12 → 0.7.0 upgrades.
