MCP server for email client HTML/CSS compatibility, backed by caniemail.com
Four well-named, read-only tools with comprehensive descriptions (194 - 500+ chars each). All tools have explicit input schemas with typed parameters and descriptions. Output schemas are documented in descriptions. Strong naming convention (verb_noun: lint_email, check_feature_support, search_features, data_source). Parameter descriptions are detailed and include constraints (e.g., client wildcard patterns, feature slug guidance). No destructive operations, so error handling focuses on validation and guidance. Main gaps: no tool annotations (readOnlyHint present implicitly via Risk field, but not in MCP schema), no pagination support (not needed for these tools), and descriptions could be more concise for LLM token efficiency.
Per-client support verdict for a single feature, for deciding HOW to build something rather than checking what you already built. Returns one of four verdicts per client: supported, unsupported, mitigated (works with a documented workaround — read the notes), or untested (no data; not the same as unsupported). Also returns the version the verdict came from, every version on record, and how stale the last test is. Feature slugs are not guessable — use search_features first.
Describe the caniemail dataset in use: whether it is live, cached, or bundled; when it was last updated; and whether it is stale. Call this to decide whether to warn a user that answers may be out of date.
Check drafted email HTML and/or CSS against email clients and report only what breaks. Call this after writing an email, before sending. Returns findings at three severities: "error" (unsupported — will not render, use a fallback), "warning" (partial or conditional support — read the notes, usually workable), and "unknown" (never tested on those clients — this is NOT evidence of support; avoid or test). Passing features are never returned. One feature usually yields two or three findings, one per verdict, and a finding carries only what its verdict decides: severity, verdict, clients_affected, client_count, notes and feature_notes. Everything else is in the result's "features" legend, keyed by the slug each finding's "feature" names — look there for the title, the feature URL, last_test_date, and "positions", every place your markup uses it as "line:col-line:col" (with occurrence_count, which is higher than the list when a feature appears more than ten times). "clients_affected" is compressed against the clients you asked for: "*" means all of them and "outlook.*" means all the ones you asked for in that family, with client_count always the exact number. Per-severity advice is in the "guidance" legend rather than repeated on every finding.
Tool annotations missing from MCP schema. Risk field in source code (READ_ONLY) is not exposed as readOnlyHint in tool definition, preventing clients from marking these as safe for concurrent execution.
Description length for lint_email (500+ chars) exceeds LLM-optimal range (50 - 200 chars). While comprehensive, it wastes tokens and buries key intent. Recommend: move detailed guidance to a separate 'guidance' field or split into summary + details.
check_feature_support parameter 'version' description is long and complex. LLMs may misunderstand wildcard behavior or version pinning semantics. Recommend: add an enum of common versions or simplify with examples.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2026-07-28+ | v2 |
Search the caniemail feature list by keyword and return matching slugs with one-line descriptions. Start here: slugs are not guessable — "rounded corners" is "css-border-radius" and flexbox is "css-display-flex". Returns identifiers only, never support data, so it is cheap to call speculatively.
search_features returns 'identifiers only' per description, but output schema is not explicitly documented in source. Callers must infer structure from description text alone.
data_source tool has empty input schema (no parameters), but description does not explicitly state this. LLMs may attempt to pass parameters unnecessarily.