feat(http): serve landing page at GET / with icon link tags#60
Merged
Conversation
Closes the browser-side favicon-discovery gap. Before this change,
hitting `/` returned Express's default 404 HTML with an empty
<head>, so any client doing HTML-shaped icon discovery (favicon
checkers, some connector UIs that walk <link rel="icon"> tags)
found no hints and fell back to nothing.
Pairs with the v1.6.6 URL-form serverInfo.icons fix so we now
cover BOTH icon-discovery channels:
- MCP protocol channel: serverInfo.icons emits URL first (data
URI fallback second) — for clients reading the initialize
response.
- HTML channel: GET / returns a small HTML page with proper
<link rel="icon" type="image/svg+xml" href="/icon.svg"> and
<link rel="apple-touch-icon" href="/icon.svg"> — for clients
that crawl the root URL like a browser.
The HTML is static bytes: no template interpolation, no user input,
no XSS surface. Generic content only — mentions /mcp, the GitHub
repo, and the license; no deployment-specific URLs or tenant info.
1h Cache-Control (shorter than the icon's 24h so copy fixes
propagate within the day).
Also adds a human-friendly side benefit: if a dev hits the URL in
a browser by mistake they see a useful "what is this?" page
instead of Express's "Cannot GET /" default.
6 new tests in tests/http-app.test.ts: content-type, link-tag
shapes for both <link rel="icon"> and apple-touch-icon, /mcp
reference in body, cache-control header, and a determinism check
(two requests return identical bytes — catches any future
template-interpolation refactor mistake).
527 → 533 tests. Bundle 165.75 KB stdio (unchanged) / 192.24 →
193.63 KB http (+1.39 KB for the landing-page HTML constant).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6 tasks
arapov
added a commit
that referenced
this pull request
May 29, 2026
…hanism (#61) PR #59 emitted a URL-form serverInfo.icons entry on the theory that Claude.ai's connector UI reads serverInfo.icons and was skipping our data: URI. Empirical investigation (loomiomcp vs capsulemcp log A/B) disproved that theory: - Claude.ai's connector icon comes from a favicon crawler (AWS-hosted, hourly, desktop+mobile Chrome UAs) that fetches /favicon.ico DIRECTLY. It never reads serverInfo.icons, and never parses the landing-page <link> tags. - It only crawls CUSTOM DOMAINS, not Public-Suffix-List platform subdomains (*.run.app, *.vercel.app, *.herokuapp.com). That's why the sibling loomiomcp on mcp.openssl-communities.org shows an icon and capsulemcp on *.run.app doesn't. So #59 was built on a wrong hypothesis and is not load-bearing. It was harmless (spec-correct, would only ever matter IF Anthropic ships serverInfo.icons consumption per modelcontextprotocol#152), but it added a helper + refactor justified by a disproven premise. Reverting for cleanliness restores the simple `icons: ICONS` data-URI shape that predated #59: - build-icon.mjs re-emits the ICONS export; icon.ts regenerated. - src/icon-builder.ts + tests/icon-builder.test.ts deleted. - server.ts back to `import { ICONS }` / `icons: ICONS`. Kept: PR #60's landing page (independent DX merit — human-readable root + favicon-checker support), but its CHANGELOG entry is corrected to NOT claim it fixes Claude icon display, with a scope note documenting the real favicon-crawler + custom-domain mechanism. The actual fix for the connector icon (custom-domain mapping) is infra-side, not a code change in this repo. 533 → 529 tests (−4, icon-builder tests removed). Bundle 165.18 KB stdio / 193.05 KB http. HOWTO test count + bundle figures resynced (were stale at 523 / ~191 from earlier un-synced merges). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the browser-style favicon-discovery gap. Before:
GET /returned Express's default 404 with an empty<head>— favicon checkers and any connector UI that walks<link rel="icon">tags found no hints and fell back to nothing. After:GET /serves a small static HTML page with proper icon link tags + a human-readable body.Pairs with the URL-form
serverInfo.iconsfix shipped earlier so both icon-discovery channels are covered:serverInfo.iconsin theinitializeresponse/, parses<head>, follows<link rel="icon">The page
Static HTML, ~1.2 KB. Generic content — no deployment-specific URLs or tenant info.
Cache-Control: public, max-age=3600(1h — shorter than the icon's 24h so copy fixes propagate within the day).Security
PUBLIC_BASE_URL, no tenant names, no internal infra references.response bytes match across two requests) catches any future refactor that introduces interpolation.Test plan
tests/http-app.test.ts):<link rel="icon" type="image/svg+xml" href="/icon.svg">is present<link rel="apple-touch-icon" href="/icon.svg">is present/mcp(so a dev hitting the URL gets a next-step pointer)public, max-age=3600Side benefit
Devs who hit the URL in a browser by mistake now see a useful "what is this?" page instead of "Cannot GET /". Improves the dev-experience when troubleshooting deployment URLs.
🤖 Generated with Claude Code