# QRCO MCP — color palettes, brand systems and contrast tools QRCO is a color palette generator and color scheme tool for designers and AI assistants. Generate palettes, build light and dark brand color systems, measure WCAG 2.x contrast, inspect color values, find approximate digital swatch matches, and export CSS, Tailwind CSS v4, SCSS, JSON, SVG, Adobe ASE or DTCG tokens through MCP. Website: https://qrco.ca/ Connection guide: https://qrco.ca/connect ## Connect - Public color tools: https://qrco.ca/mcp — Streamable HTTP, no login or API key. - Private saved library: https://qrco.ca/mcp/account — separate Streamable HTTP connection with OAuth. Keep the public connection for generation. - Account connection management: https://qrco.ca/agent-access Google sign-in identifies the user to QRCO; no Gmail access is granted. Users sign in and approve on QRCO's site. Never request passwords, login codes or tokens in chat. QRCO supports public-client dynamic registration, authorization code with PKCE S256 and rotating refresh tokens. The resource is https://qrco.ca/mcp/account; omission defaults exclusively to this resource for generic OAuth compatibility. Explicitly different resources are rejected. ## Public tools (22) - get_capabilities: current connection, actual tools/scopes, schema limits and account guidance. - present_palette: 2–10 exact colors in the native Brand/Balance/System workspace on compatible hosts; no saving or publishing. - generate_palette: 2–10 colors, one generation mode and optional fixed colors. - generate_palette_set: compare 1–5 generated directions in one call. - random_palette: generate a random palette. - inspect_color: HEX, RGB, HSL, HSB, Lab, CMYK and nearest digital swatch approximations. - vary_palette: produce variations from existing colors. - match_swatch: nearest digital Pantone® approximation and color-distance result; verify physically for print. - check_contrast: WCAG 2.x text contrast for two opaque sRGB colors. - audit_palettes: audit 1–10 identified palettes with actual backgrounds, explicit index pairings and summary/failures/full output. - compare_palettes: compare 2–5 palettes against a baseline using Delta E 2000, contrast opportunities and exact anchor retention; no automatic winner. - audit_palette: measured contrast across palette pairs and black/white foreground suggestions. - create_studio_link: carry colors, palette title and custom color names into Studio; does not save. - export_palette: return CSS, Tailwind CSS v4, SCSS, JSON, SVG, Adobe ASE or DTCG content; useNames enables named keys. Does not write files. - search_explore: browse the public Explore catalog by name or exact HEX. - check_color_vision: simulate selected color-vision conditions, report pair separation and heuristic review flags; no accessibility certification. - generate_state_colors: draft filled-control interaction states and focus colors with actual-surface checks, disabled exclusions and CSS/Tailwind tokens. - generate_tonal_scale: derive QRCO 50–950 stops with OKLCH/sRGB gamut handling, original seed token, contrast checks and CSS/Tailwind exports. - build_brand_system: derive light, dark or dual-mode roles, measured pairings, semantic CSS and Tailwind exports. - extract_image_colors: bounded flat RGBA pixels → representative HEX colors, QRCO names and pixel coverage percentages. - adjust_color: one-color transformations with explicit per-operation amounts and clipping reports. - generate_gradient: interpolate in srgb, srgb-linear, oklab or oklch; steps is the total returned color count. ## Account tools (up to 10, depending on approval) Read scope, palettes:read: - get_capabilities: inspect granted scopes and available account tools. - search_explore_for_account: Explore retrieval with saved-library similarity exclusion. - list_saved_palettes: returns up to 25 palettes per page. Follow nextOffset until null before claiming a total. - get_saved_palette: saved colors, custom names, brand roles and measured contrast, plus publication state. - preview_brand_revision: preview stronger contrast or a missing mode while preserving palette colors and anchors. No save. - get_palette_history: version summaries or an exact tracked snapshot. Follow nextBeforeVersion for older pages. Optional permissions: - palettes:save enables save_palette, for private saving. - palettes:publish enables publish_palette and unpublish_palette, only on explicit user instruction. - palettes:revise enables save_brand_revision, after reviewing the exact preview/version/hash. Earlier versions are retained. Existing grants do not gain new write permissions; obtain fresh approval. Revision and private saving do not update a public page. History starts with tracked edits, cannot reconstruct previously overwritten states, and is deleted when the palette is deleted. Opening an old version does not automatically restore it. Write retries use the same requestId and identical arguments; a replay receipt is not proof of current state. ## Example requests - Build a warm coffee brand palette and light/dark semantic color roles. Return CSS and Tailwind tokens. - Find my saved Copper & Counsel system, then preview adding dark mode without changing its palette colors. Do not save yet. - Audit these colors for the listed text/background pairings and explain any failures. ## Boundaries Contrast results cover the stated color pairings, not complete accessibility certification or color-vision-deficiency simulation. Material modes are creative adjustments, not measured fabric/ink/lighting predictions. Pantone®/RAL matches and CMYK values are digital approximations, not physical print specifications or proof of a licensed library. Trend generation uses a stored model, not live popularity tracking or forecasting. Image extraction requires client-prepared pixel samples; QRCO does not fetch image URLs/files. Cultural research, trademark clearance and batch exporting are not offered. Custom names are supported; evocative naming is not guaranteed. Public tools and account access are free during the beta. A user's AI app may have separate charges. Directory acceptance and search placement are not guaranteed. Muse account listing and retrieval have been user-tested; compatibility is client-specific. ## Comparison and batch audit (v1.1.0) Provide palettes as objects with a unique id, colors and optional name/names. Optional pairings use zero-based foregroundIndex/backgroundIndex and minimum (default 4.5). Shared backgrounds use id, color and minimum. Up to 10 colors per palette, 4 backgrounds and 20 pairings per palette. Any invalid item rejects the entire call. The default detail is summary; failures adds failed requested checks and similar pairs, full includes all measurements. No requested checks means allRequestedChecksPass is null. Pairwise contrast opportunities are not a whole-palette pass. Compare matching defaults to unordered (bidirectional nearest-neighbor, many-to-one); indexed requires equal lengths and compares corresponding roles. baselineIndex defaults to 0. Optional anchors test exact color retention. Delta E 2000 uses standard weighting and is not capped at 100; similarityThreshold defaults to 2 as a configurable near-duplicate heuristic. All threshold decisions use unrounded values. Neither tool simulates color-vision deficiency, chooses an aesthetic winner, saves or publishes. ## Tonal scales (v1.2.0) Call generate_tonal_scale with seed, optional prefix (default brand), optional unique stops from 50/100/200/300/400/500/600/700/800/900/950 and up to four backgrounds {id,color,minimum}. Minimum defaults to 4.5 and accepts 3–21. Subsets are sorted ascending and retain the same values as the full scale. The exact input color is returned separately as prefix-original. Generated stops use QRCO fixed OKLCH lightness targets, seed hue/chroma and chroma reduction where needed to fit sRGB. Stops need not match the seed, even at 500. Neutral seeds remain neutral. Final HEX colors have measured white/black and supplied-background contrast, recommended black/white text and gamut diagnostics. CSS and Tailwind CSS v4 are included. Stop numbers are not accessibility guarantees, Tailwind built-in colors or equally spaced color differences. This is a bounded fixed-hue/lightness chroma-reduction method, not the CSS Color 4 gamut-mapping algorithm. Measurements use final 8-bit sRGB colors. Full 11 stops are not a Studio palette; select at most 10 stops for palette tools. No save, publication or external request. ## Argument and save contract (public v1.2.1 / account v1.0.1) Unknown arguments are rejected instead of silently discarded. Use the refreshed tool schemas: `generate_palette.size`, `generate_palette_set.paletteCount` and `colorsPerPalette`, `random_palette.count` (colors in ONE palette), `create_studio_link.name`, `export_palette.format`, and `compare_palettes.baselineIndex` (zero-based index, not an ID). `vary_palette` has no `variants` filter. Neither generator accepts a free-text `brief`; the assistant maps the brief to supported controls. Choose at most one generation mode. `baseColor` constrains unlocked colors to the requested family; use `lockedColors` for an exact anchor. Recognized families: red, orange, yellow, green, teal, blue, purple, pink, brown, neutral. For `save_palette`, supply 2–10 six-digit HEX values with `#`; uppercase and lowercase are accepted and stored uppercase. To attach roles, copy **the `system` field** from the public `build_brand_system` response into `brandSystem`, and use `system.palette` as `colors`. Do not pass the whole response or just `{mode, roles}`. Supported persisted shapes: | Mode | Required fields | | --- | --- | | light | `version: 1`, `mode: "light"`, `palette`, `roles` | | dark | `version: 2`, `mode: "dark"`, `palette`, `roles` | | both | `version: 2`, `mode: "both"`, `palette`, `roles` (light), `darkRoles` | Every roles object contains exactly `background`, `surface`, `text`, `mutedText`, `primary`, `onPrimary`, `accent`, `onAccent`, `border`, `focus`. The palette must match saved colors in order. Primary/accent must be palette members and agree across modes; measured role contrast must pass validation. A system's format `version` is distinct from a saved palette's revision `version`. `preview_brand_revision` requires an existing saved `metadata.brandSystem`. Preview options may be omitted or partial; the result returns their complete normalized form. **Saving requires all four returned option fields** (`addMissingMode`, `textContrast`, `uiContrast`, `lockedRoles`), plus the preview's `id`, saved-palette `version`, `previewHash`, and a fresh UUID `requestId`. Copy them; do not rebuild options from the initial request. Only save a reviewed canSave:true preview; changes may be roles or targetChange. Changed inputs or palette versions require a new preview. Reuse identical arguments/requestId only to retry the same save. No publish occurs. HEX ratios concern opaque sRGB colors. They do not predict tattoo healing/appearance, physical skin, fabric, paint or lighting. Nearest Pantone® catalog results need physical proofing with the production provider; they are not production approval or trademark clearance. Neutral tonal seeds intentionally yield neutral ramps. ## Brand colors used as links (public v1.3.0 / account v1.1.0) `build_brand_system` and `get_saved_palette` (when a valid brand system is attached) now return `usageChecks` separately from existing `pairings`. This tests primary and accent as normal-sized link text against **both background and surface in each stored mode**, at 4.5:1. Four checks per mode; eight for dual mode. An existing system can pass all button/body/outline pairings and still fail these link uses. Inspect `failureCount`, `originalColorsPassAllListedSurfaces`, and `modes[].links[]`. Each link has its original HEX, per-surface checks, `passesOnAllListedSurfaces`, and a `status`. Failures may include `suggestedLinkColor` with a HEX, measured checks and `applied: false`. A passing link needs no suggestion. `no-suggestion-found` means the bounded search found none, not proof that no color exists. Example: primary #767676 passes on white at 4.5422:1 but fails on its generated #FBFBFB page at 4.3895:1. The light-mode suggestion #747474 passes both. Dark mode is checked independently and may need a different shade. Pass/fail uses unrounded measurements. Suggestions are separate colors for review, obtained by bounded mixing toward black or white and least squared RGB change among sampled passing candidates. They do not modify anchors, saved roles, history, the Studio URL or CSS/Tailwind exports. No schema migration, new scope or new tool is needed. Existing revisions still operate on the existing ten roles; suggestions are not saved link roles. Use a persistent non-color cue such as an underline for inline links. Surrounding body-text contrast is informational; this audit does not certify link identification or focus behavior. See [W3C Use of Color](https://www.w3.org/WAI/WCAG22/Understanding/use-of-color) and [Contrast Minimum](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum). Other surfaces, overlays, images and hover/visited states are outside this check; use explicit `audit_palettes` background/pairing inputs or `check_contrast` for other opaque HEX pairs. ## Named base families (public v1.3.1) `generate_palette` and `generate_palette_set` now constrain all generated **unlocked** colors to the requested `baseColor` family. The engine uses explicit sRGB HSL bounds, handles red across 360°/0°, and rechecks family membership after material styling. The previous generator incorrectly treated circular red as a linear interval and fed HSL-style family angles into LCH. Derived color-name hue classification now uses HSL too; existing saved/custom names are untouched. Responses include `metadata.baseColorConstraint`: requested family, color space, hue/saturation/lightness bounds, `unlockedColorsInFamily`, and `lockedExceptions`. Exact user locks override the family and remain exact even with material styling. For example, a locked green in a red palette remains green and is reported as an exception. Unlocked red swatches stay within hue345–15°, saturation0.60–0.95, lightness0.28–0.52. These are QRCO product-defined family ranges, not universal color naming or a safety standard. This makes the family request enforceable; it does not assign danger/status meaning, guarantee contrast or simulate physical materials. Check the actual foreground/background pairs and use appropriate labels/icons. A stronger signal or icon does not automatically repair insufficient text contrast. `brief` and `variants` were already absent from the preceding MCP schemas; strict validation now rejects them instead of ignoring them. `vary_palette` already required 2–10 colors. Brand-system `pairings` already include focus/background and focus/surface checks; geometry, occlusion and full focus accessibility remain outside those measurements. ## Pantone® option by tool `includePantone®` is accepted by `generate_palette` and `inspect_color`, but **not** by `generate_palette_set`. Omit it from batch-generation calls; unknown arguments are rejected. To obtain a nearest Pantone® catalog match for a chosen batch color, call `match_swatch` with its `hex`. This documents the current schemas; it does not add batch matching or certify physical print results. ## Save identity and revision readiness (account v1.1.1) `save_palette` deduplicates within the authorized account against **current stored content**: normalized palette name, ordered colors, and complete metadata. Normalization trims palette/custom-name whitespace, uppercases HEX colors and canonicalizes object-key order. Array order matters. Custom color names, the complete brand system, and Studio metadata (`locks`, `style`, `harmony`, `source`) participate. Missing metadata is not equivalent to an explicitly populated field. Two entries with the same palette name and HEX values may therefore be distinct. No automatic merging or deletion occurs. `requestId` is separate retry protection. For the same action, reuse the same UUID and identical arguments; a replay returns the original receipt. With a new UUID, identical normalized current content returns the existing palette ID with `existing:true`. If the original palette was subsequently renamed or revised, saving its old payload may create a new palette. Deleting it also removes the current-content match. There is no `description` parameter on private `save_palette`; public descriptions belong to the explicit publication flow. For `preview_brand_revision`, provide the saved palette's `id` and current `version`. Inspect the new additive fields: | status | canSave | Meaning | | --- | --- | --- | | `ready-to-save` | true | Feasible changes exist; review before committing. | | `unchanged` | false | Existing roles already satisfy the minimums; no commit is needed. | | `infeasible` | false | Requested constraints cannot be met by the adjustment method. | `feasible:true` means the constraints are satisfied, not that anything changed. Lowering a contrast target does not lighten roles or restore a previous version. `saveMessage` explains the outcome. Commit only a reviewed `canSave:true` result with the exact returned `id`, `version`, complete normalized `options`, `previewHash`, and a fresh request UUID. Re-preview after v1.14.0: measured pair details and persisted targets change the hashed result. An unchanged commit is rejected without creating history or advancing the version. ## Swatch match quality (public v1.4.0) `match_swatch` and `inspect_color` now return `distanceMetric` and `matchQuality` alongside each nearest catalog result. Pantone® ranking retains **CIE76**; RAL retains **CIEDE2000**. These numeric distances are not interchangeable. Existing nearest matches and displayed distances are unchanged. `success:true` means the lookup completed, not that a close match exists. Inspect `matchQuality.band`, `isCloseMatch`, and `warning` before recommending a swatch. QRCO's conservative review bands are: near ≤2, approximate >2–5, distant >5–10, poor >10. They are product heuristics, not industry acceptance tolerances. Decisions use the unrounded `evaluatedDeltaE`; approximate, distant and poor results carry warnings. Even near matches have `physicalProofRequired:true` for the intended substrate, finish and lighting. For example, `match_swatch({"hex":"#FF00FF"})` currently finds a nearest Pantone® swatch at CIE76 ΔE 30.686, explicitly marked `poor` and `isCloseMatch:false`. Report it as a poor catalog approximation, not a production-ready match. No physical rendering or trademark clearance is implied. ## Repeatable variations (public v1.5.0) `vary_palette` accepts optional `referenceColors`: retain the original HEX array, in the same count and role order as `colors`, and send it on every call. All alternatives are generated from that reference instead of the latest input. Keeping the same reference, material and chosen variation ID produces the same colors across repeated calls; switching materials or references deliberately changes the result. Without `referenceColors`, existing relative behavior remains: Soft adds lightness and reduces chroma; repeated Soft/Muted choices can accumulate lightening/desaturation. Each returned palette now includes `variationContext` with `mode`, normalized `sourceColors`, measured `meanLightnessChangeFromSource` (CIELAB L* units, not ΔE), a cumulative-transform warning in relative mode, and a note explaining reference mode. The server cannot infer or remember an omitted original. This does not preserve individual anchors or certify contrast; use `compare_palettes` and explicit pairing checks to review alternatives. Example: `vary_palette({"colors":["#7D4B3F","#A65F40","#292321","#F3EBDD"],"referenceColors":["#7D4B3F","#A65F40","#292321","#F3EBDD"],"count":3,"material":"digital"})`. For a follow-up, replace `colors` with a result while keeping `referenceColors` unchanged. No `variants` filter is supported; select a returned `id`. ## Brand construction transparency (public v1.6.0) `build_brand_system` now returns `construction` and `unreferencedColors` outside the saved `system`. Primary defaults to index 0; accent defaults to the next index (wrapping to 0). Explicit `primaryIndex`/`accentIndex` override these choices. Light-mode text and muted text start from the darkest input by relative luminance; dark mode starts from the lightest. Ties use the first input. Other roles derive from these selected sources; light surface is fixed white, and on-colors choose black or white by contrast. `construction.anchors` records the selected indices. Each entry in `construction.modes` reports its text seed and every role's `origin` (`input`, `derived`, or `constant`), `sourceIndices`, and `matchesInputIndices`. A derived role may equal an original HEX; exact equality is reported separately from how it was generated. `unreferencedColors` lists input indices not selected as anchors or text seeds across the requested modes. They are retained in the full palette and Studio link, and may happen to equal a derived output. This is not a recommendation to discard them. Generation, contrast checks, exports and the saveable `system` shape are unchanged. Save `result.system` as before; the construction explanation describes this build, not the history of later saved revisions. ## Library totals and history dates (account v1.2.0) `list_saved_palettes` now returns numeric `totalCount`: all saved palettes owned by the connected account, including both private and published entries, not just the current page. The count and page come from one database statement. Page size remains 25, ordering remains newest creation first with ID as tie-breaker, and `nextOffset` is unchanged. Empty libraries report 0; an offset beyond the end returns an empty page but still reports the full total. Separate calls may observe saves/deletes, so offset pagination is not a frozen cross-call snapshot. `get_palette_history` keeps `createdAt` in epoch milliseconds and adds `createdAtIso`, an ISO 8601 UTC string ending in Z, for each version summary. Exact-version reads include both fields alongside `palette`, leaving the historical palette object unchanged. These dates describe when that version was saved; they are not necessarily the palette's original creation date. Old history availability limits and ownership checks remain unchanged. The website history API receives the same additive dates. No writes or new permissions are required. ## Current palette dates (account v1.2.1) `get_saved_palette` now returns top-level `createdAt` / `createdAtIso` for the palette's original creation and `updatedAt` / `updatedAtIso` for its current version's save time. Numeric values are epoch milliseconds; ISO strings are UTC. Compare its `updatedAt` with the current `get_palette_history` summary's `createdAt`, or with an exact history read's top-level `createdAt`. History dates describe individual versions; creation and last-save dates are deliberately distinct. Reads do not modify timestamps, snapshots, roles, versions or publication. ## Version change summaries (account v1.3.0) `get_palette_history` now includes `changeSummary` in every version summary and alongside `palette` for exact-version reads. It compares that saved snapshot only with version N−1. No palette, timestamp, role or stored history is modified. The report contains `status`, `comparedToVersion`, readable `text`, and structured `changes` with stable `type` labels. It identifies palette renames, changed color positions (including added/removed positions), color names, locks, metadata fields, brand-system additions/removals, added/removed light or dark modes, and changed roles within existing modes. A new mode is an addition, not a claim that all its roles were edited. HEX arrays are compared by index, so reordering counts as changed positions. Version 1 reports `initial`. A missing immediately preceding snapshot reports `previous-unavailable`; no comparison is made against a more distant version. `compared` may report no saved content changes. Changes do not establish who edited a palette, why, or whether it improved. Page-boundary comparisons include the already fetched lookahead row, without a query per version. Historical palette objects remain unchanged and user text remains untrusted data. ### History summary status/type contract (account v1.3.1) Branch on `changeSummary.status` and `changes[].type`, not the readable `text` or `message`. | Status | Meaning | | --- | --- | | `initial` | Version 1; no predecessor comparison. | | `compared` | Compared with version N−1; an empty changes array means no saved content differences. | | `previous-unavailable` | Version N−1 is unavailable; no changes are inferred. | Current change types: `renamed`, `colors-changed`, `color-names-changed`, `locks-changed`, `metadata-changed`, `brand-system-added`, `brand-system-removed`, `mode-added`, `mode-removed`, `roles-changed`, `brand-palette-changed`, `brand-format-changed`, `contrast-target-changed`. `colors-changed` includes zero-based `indices`, `previousCount`, `currentCount`; name/lock changes include `indices`. `metadata-changed` includes `field`. Mode changes include `mode`; role changes include `mode` and `roles`. Format changes include `from` and `to`. Simple rename/system/palette change entries need no extra fields. Clients should handle an unfamiliar future type by displaying its message rather than dropping the whole summary. ## Interaction-state colors (public v1.7.0) `generate_state_colors` creates a deterministic draft for an opaque filled control. Required: `baseColor`, `surface` (actual background). Optional: `foreground` (exact text color for enabled states), `focusColor` (exact ring color), `focusAdjacentColors` (up to four additional touching colors), `textMinimum` (4.5–7, default4.5), safe token `prefix` (default `brand`). Surface is always included in focus checks. No modes, hover overrides or saved-system input are accepted. Default keeps the original HEX. Hover, active/pressed and selected use fixed OKLCH lightness offsets with chroma reduction into sRGB; selected is not a semantic substitute for hover. Without a fixed foreground, each state chooses black or white. Enabled text checks use the requested minimum; fill-to-surface checks use3:1 for uses where the fill conveys the control/state. They are pair measurements, not automatic interface violations or certification. Failed checks stay visible; the base is never silently repaired. Focus checks only declared adjacent colors. Add the actual control fills when the ring touches them; otherwise an assumed offset gap must really exist. Automatic focus checks the base then257 lightness samples, selecting a passing candidate closest in OKLCH lightness; this is bounded search, not global optimization. Supplied focusColor is unchanged even when it fails. If no candidate passes all listed surfaces, focus.color is null, status is unresolved, the summary fails overall, and no focus token is exported. Rendered focus area, occlusion and same-pixel focused/unfocused contrast are outside the tool's scope. Disabled text/surface checks are informational: `minimum:null`, `passes:null`, excluded from summary totals. This exemption applies only to genuinely inactive controls. Colors are opaque mixtures for the specified surface, not CSS opacity. Between-state contrast is also informational; use labels/icons and real interaction testing for distinguishability. The result returns `states`, `focus`, a measured `summary`, warnings, CSS custom properties and Tailwindv4 `@theme` tokens. Exports contain draft tokens even when checks fail (except unresolved focus); inspect summary before using them. No account save, publication or existing brand role is changed. Example: `generate_state_colors({"baseColor":"#A65F40","surface":"#FFFFFF","focusAdjacentColors":["#A65F40"],"prefix":"copper"})`. Sources: [text contrast and inactive controls](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html), [non-text contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html), [focus appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance.html). Adjacent-color contrast concerns1.4.11;2.4.11 concerns occlusion. HEX pairs alone do not establish2.4.13 focus appearance compliance. ## Color-vision review (public v1.8.0) `check_color_vision` simulates a palette and measures pair separation in the resulting display colors. Required `colors`: 2–10 opaque 3/6-digit HEX strings (either case, optional #). Optional `names`: exactly one nonempty label per color, max100 characters. No saved-library permission is needed; it never saves, changes colors or modifies brand roles. - `types`: unique subset of `protanopia`, `deuteranopia`, `tritanomaly`; default all three. Fixed Machado severity1. The last is explicitly **severe tritanomaly approximation, not tritanopia**. `tritanopia`, achromatopsia and custom severity are unsupported and rejected. - `reviewThreshold`: 1–20, default5. Flags use **unrounded simulated CIEDE2000 < threshold**. This is a QRCO review heuristic, not a validated threshold for CVD discrimination or a WCAG requirement. - `includeAllPairs`: defaultfalse. Always returns `flaggedPairs`; true also returns every `pairs` measurement (max45 per simulation). `original` preserves normalized HEX, order and names. Each `cvdChecks` entry carries its type/label/severity, `simulatedColors` with clipping diagnostics, pair counts and flagged pairs. Pair indices are zero-based input positions; results stay in index order. Measurements include `originalDeltaE2000`, `simulatedDeltaE2000` and `distanceChange` (simulated minus original; negative means reduced separation), rounded to four decimals for display. Flags use raw values. Full `pairs` entries carry `flagged` (boolean) and `reason`; `flaggedPairs` is the subset with `flagged:true`, retaining the same fields. There is no `flag` field. Pair `reason`: `original-duplicate` for identical original HEX; `already-close` for a flagged pair below threshold before simulation; `newly-close` for a flagged pair that crosses below threshold; `not-flagged` otherwise. `identicalSimulatedHex` identifies equality after display rounding. Summary `flaggedPairEvaluationCount` sums across simulations, so a pair can be counted three times. No pass/fail or whole-palette accessibility verdict is returned. No flags does **not** guarantee distinguishability. Method: decode sRGB to linear RGB, apply published Machado/Oliveira/Fernandes matrices, clip channels to [0,1], encode and round to 8-bit HEX. `gamutClipped`/`clippedChannels` report out-of-range channels, including tiny matrix precision overshoots. Clipping and rounding can reduce separation. CIEDE2000 uses CIELAB D65 and standard weights on final displayed HEX; this metric is not validated as a measure of what a person with CVD can discriminate. Simulations approximate selected conditions and cannot reproduce every person's experience. Use labels, icons, patterns or position for meaning. Run `check_contrast` separately on original text/background colors. No contrast-under-simulation WCAG claims, physical material predictions or automatic fixes. Existing contrast and brand-system tools are unchanged; these checks are a separate public tool, not embedded in saved reads. Example: `check_color_vision({"colors":["#FF0000","#00AA00","#FFFFFF"],"names":["Stop","Go","Paper"],"includeAllPairs":true})`. Red/green becomes a newly-close pair under deuteranopia (simulated ΔE2000 about2.5798); use additional cues. Sources: [Machado et al. paper](https://doi.org/10.1109/TVCG.2009.113), [published matrix values via Colour Science](https://raw.githubusercontent.com/colour-science/colour/develop/colour/blindness/datasets/machado2010.py), [Colour's documented tritanomaly limitation](https://colour.readthedocs.io/en/latest/_modules/colour/blindness/machado2009.html). ## Correction: legacy accessible generation (public v1.8.1) `generate_palette` still accepts `accessible:"aa"` or `accessible:"aaa"` for compatibility. These now mean a **normal-text contrast target for consecutive palette pairs**: [0,1], [1,2], … (no wraparound), at4.5:1 or7:1 respectively. They do not certify a palette, arbitrary pairings, CVD accessibility or a finished interface. `generate_palette_set` does not expose this parameter; do not invent it. The backend generation/set/random paths share the correction when this mode is selected. The old unconditional `metadata.accessible:true` has been removed. `metadata.accessibility` remains the requested level, never evidence of success. Read `metadata.contrastTarget`: - `scope:"adjacent-pairs"`, `requestedLevel`, `minimum`, `status:"met"|"unmet"`, `allDeclaredPairsPass`, `pairCount`, `failureCount`. - `pairs`: exact final colors, zero-based indices, displayed four-decimal ratio, target minimum and `passes` determined from the **unrounded** ratio. - `lockedIndices`, `adjustedIndices`, bounded `search` details and explicit limitations. Generation first creates seed colors and applies material styling. Exact locks are then restored. If the final adjacent pairs fail, QRCO searches two alternating dark/light directions, mixing unlocked colors toward black/white in256 bounded sRGB steps per direction. It takes the first passing step, testing dark-first before light-first at ties. This is not a minimum-change optimizer and adds no hidden contrast buffer. See optional contrastMinimum below to request a higher target. A passing result can be very close to the target; recheck after downstream color changes. With no locks, alternating black/white endpoints guarantee a passing candidate. Locks can leave a target unmet; no passing candidate means the post-material colors and exact locks are retained, `status:"unmet"`, and actual failures remain visible. No global infeasibility claim is made. Adjustments can change the material style's appearance. Always choose the actual foreground/background pairing and check it with `check_contrast`. API `success:true` means generation completed, not that a locked contrast target was achieved. Old saved or published palettes are not rewritten. Short HEX locks now normalize correctly instead of being silently skipped. Example: `generate_palette({"accessible":"aaa","size":5,"material":"fabric"})`; inspect all four declared pairs. Adversarial example: `generate_palette({"accessible":"aaa","size":2,"lockedColors":[{"index":0,"hex":"#777777"},{"index":1,"hex":"#777777"}]})` must preserve both locks, report ratio1 and statusunmet, with no blanket accessible flag. Threshold references: [W3C normal-text AA](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html), [W3C normal-text AAA](https://www.w3.org/WAI/WCAG22/Understanding/contrast-enhanced.html). These requirements concern text/background usage, not palette certification. ## Optional contrast headroom (public v1.9.0) `generate_palette`, `vary_palette` (v1.10.0), and measurement-only `export_palette` (v1.11.0) accept optional numeric `contrastMinimum` together with `accessible`. Example: `{"accessible":"aa","contrastMinimum":4.8,"size":5}` or `{"accessible":"aaa","contrastMinimum":7.5,"size":5}`. Minimum allowed is4.5foraa or7foraaa; maximum21. Missing accessible, a lower target, conflicting generation modes and invalid numbers are rejected. No custom headroom parameter on `generate_palette_set` or `random_palette`; those defaults are unchanged. Omitting the option preserves existing generation exactly. With it, the existing bounded search targets the requested ratio on final adjacent pairs, after material styling and exact-lock restoration. The first passing candidate is used; no additional hidden buffer is added. Larger targets can change colors more or leave a locked target unmet. This is not a color-drift guarantee; remeasure downstream colors and actual text/background usage. `metadata.contrastTarget.minimum` and pair `minimum`/`passes` now refer to the chosen generation target (unchanged when omitted). Additive fields distinguish the standard from the optional target: - Report `standardMinimum` (4.5or7), `requestedHeadroom` (chosen minimum minus standard), `allStandardPairsPass`. - Each pair: `standardMinimum`, `meetsStandard`, `marginAboveStandard`, `marginAboveTarget`. - Margins are signed **contrast-ratio differences**, not percentages, rounded to four decimals for display. All pass decisions use unrounded ratios. A displayed zero margin can hide a tiny positive/negative value; use the booleans for the verdict. Example with exact locks #767676/#FFFFFF and aa/contrastMinimum4.8: measured4.5422, `meetsStandard:true`, `passes:false`, standard margin+0.0422, target margin−0.2578, report `status:"unmet"` and `allStandardPairsPass:true`. Both colors remain exact. Standard compliance and requested headroom are deliberately separate. Saved palettes are not rewritten. ## Contrast-aware variations (public v1.10.0) `vary_palette` accepts optional `accessible:"aa"|"aaa"` and `contrastMinimum` using the same adjacent-pair contract as generation. Supply them explicitly on **every call**; input HEX and referenceColors do not carry a prior target. The original defaults and variation operators remain unchanged when omitted. Each variation returns `contrastContext` and `contrastTarget`: - Without accessible: contrastContext.status=`not-requested`, inherited=false, an explicit warning that prior AA/AAA/headroom can be broken, and contrastTarget=null. Audit actual pairs before use; no implied carry-forward. - With accessible: contrastContext.status=`explicit-target` (a request marker, not a verdict), inherited=false, and a newly measured contrastTarget report. Read its met/unmet status, exact final pairs and standard/target margins. - Apply variation, then material styling, then bounded contrast re-targeting. This may weaken the intended style or make alternatives identical, especially at21:1. There is no uniqueness or aesthetic-preservation guarantee. - contrastMinimum requires accessible and must lie between the selected standard (4.5/7) and21. Omit it for the standard target. Returned checks apply only to consecutive pairs, never all palette pairs or a whole interface. - Fixed-reference stability requires the same referenceColors, material, operation **and contrast settings** each time. Contrast checks describe the final result, including re-targeting. Example: `vary_palette({"colors":["#000000","#959595","#000000","#959595"],"accessible":"aaa","contrastMinimum":7.5,"count":3})`. This source passes7:1, while an ordinary un-targeted Soft variation can fail it; the explicit target is re-evaluated after the variation. No account saves. The MCP variation tool does not accept locks. Its backend API's existing locked/keepLocked option preserves exact locks when an explicit target is requested; impossible targets remain unmet. ## Cross-tool contrast boundaries A palette's adjacent-pair target is not inherited by other HEX-only tools. `audit_palette` tests all pairs; a failed non-adjacent pair does not contradict a met adjacent-pair target. `build_brand_system` derives different roles and checks its own listed4.5text/3UI pairings, not the input palette's AAA/headroom target. `export_palette` now optionally remeasures and embeds an explicitly supplied target (v1.11.0); omitted targets are not inherited. Public v1.14.0 adds explicit persisted textContrast/uiContrast; see below. `random_palette` defaults to five colors when count is omitted (the old description saying random size was incorrect). A target is a requested contract; always inspect the measured verdict. ## Contrast context in exports (public v1.11.0) `export_palette` accepts optional `accessible:"aa"|"aaa"` and `contrastMinimum`. Supply the requested target explicitly, as with generation/variation. Minimum4.5foraa or7foraaa, maximum21; contrastMinimum alone, lower targets and forged contrastTarget objects are rejected. The exporter **measures, never repairs**. Passing generation results and stale reports are not trusted or inherited from HEX. The response includes `contrastContext` (`explicit-target` or `not-requested`, inherited:false) and `contrastTarget` (report or null). These are separate: explicit-target means a target was supplied, not that it passed. No option preserves existing artifact bytes; its outer warning says no contrast report was embedded. With a target, the exact same freshly measured report travels in the content: - JSON: top-level `contrastTarget` alongside name and colors. - CSS, SCSS, Tailwind v4: a leading block comment with a short warning and the report as formatted JSON. - SVG: XML-escaped JSON text inside ``. Report schemaVersion1 carries requestedLevel, standardMinimum, minimum, requestedHeadroom, scopeadjacent-pairs, met/unmet status, standard/target summaries and pair counts. Every pair includes zero-based indices, exact exported token identifiers and HEX, ratio, raw-decision standard/target booleans and signed margins. There is no last-to-first/non-adjacent guarantee. `colorChangesApplied:false` makes preservation explicit. Ratios/margins display four decimals; decisions use raw values. Token identifiers are the palette keys (e.g. brand-1); CSS adds --, SCSS adds $, Tailwind adds --color-. Names and prefixes keep existing escaping/validation; arbitrary labels are excluded from CSS comment metadata. All exports preserve original normalized colors, order and labels. Failed targets remain embedded failures, even when the standard itself passes. Example: `export_palette({"colors":["#767676","#FFFFFF"],"names":["Ink","Paper"],"accessible":"aa","contrastMinimum":4.8,"format":"css","prefix":"proof"})`. It exports the unchanged colors with ratio4.5422: standard passes, requested4.8fails, statusunmet, margins+0.0422/−0.2578. There is no recoloring to make the report pass. The report is handoff documentation, not runtime enforcement, a signature or accessibility certification. Comments/metadata may be removed by build tools or downstream applications; preserve the JSON export when a durable sidecar is needed. Recheck actual use and any edits. This v1.11.0 update did not add Adobe/Figma formats; v1.12.0 adds portable ASE/DTCG exports described below. Direct synchronization remains separate. ## Portable Adobe and Figma exports (public v1.12.0) `export_palette` adds `format:"ase"` and `format:"dtcg"`. Both preserve input order and normalized RGB colors; neither writes files, saves palettes, syncs applications, or invents brand roles/modes. Existing five formats retain their behavior. - **Adobe ASE:** `content` is **base64**, `encoding:"base64"`, `fileName:".ase"`, with a byteLength. Decode it to binary before saving. Ordered normal RGB swatches retain custom names (including Unicode); no CMYK conversion, ICC profile or spot-ink/Pantone® specification. Import through the receiving Adobe application's swatch-library controls. Duplicate labels are retained in the file; apps may rename or merge them. - **Figma/DTCG:** `content` is UTF-8 JSON, `encoding:"utf-8"`, `fileName:".tokens.json"`. Uses DTCG 2025.10 color tokens with sRGB components, alpha1 and HEX. Safe prefix-index keys become variable names; custom labels become `$description`, so duplicate names do not collide. In Figma Variables, create a new collection and drag this one JSON file into the Variables view to import one mode. Importing into an existing mode can replace matching variables. - **Measured context:** optional accessible/contrastMinimum still measures exact adjacent token pairs without changing colors. DTCG carries context in `$extensions["ca.qrco"]`. ASE cannot carry the report: its `sidecar` contains fileName, mimeType, encoding and UTF-8 JSON content with tokens and contrast context. Keep that JSON alongside the ASE, including any failed target. With no target the context explicitly says not-requested. Importers may discard extensions/descriptions; retain the source report. Example: `{"colors":["#767676","#FFFFFF"],"names":["Ink","Paper"],"prefix":"brand","format":"ase","accessible":"aa","contrastMinimum":4.8}` returns unchanged gray/white swatches and a sidecar reporting the unmet 4.8 target. Use `format:"dtcg"` for the matching token file. Format and import references: [Adobe ASE exchange](https://helpx.adobe.com/uk/illustrator/desktop/manage-colors/use-swatches/share-swatches-between-applications.html), [Figma token import](https://help.figma.com/hc/en-us/articles/15343816063383-Modes-for-variables), [DTCG color format](https://www.designtokens.org/tr/2025.10/color/). Files are tested structurally and ASE with an independent decoder; actual Adobe/Figma application import remains a manual acceptance step. Direct sync and saved brand-system mode export remain later work. ### HEX contracts (public v1.12.0; account v1.3.2) Published JSON Schema patterns explicitly include uppercase and lowercase HEX, matching existing runtime behavior. Public color arguments retain optional # and 3/6 digits; account save_palette.colors still requires # and six digits. No case/canonicalization or account permission changes. `search_explore` rejects malformed hash-prefixed queries such as `#GG0000` or `#1234` before catalog retrieval. Valid HEX matches exact normalized colors; other queries still match literal palette names. An empty result for a valid literal name is not a semantic search verdict. ## State and audit diagnostics (public v1.13.0) `generate_state_colors` now adds explicit warnings naming enabled states whose text or fill-to-surface checks fail. If all four enabled fills fail, the warning says so. These are draft colors; warnings do not recolor them or change export tokens, thresholds, existing pass flags, or totals. A failed fill check matters when that fill identifies the control/state; borders, labels and actual geometry still affect applicability. Disabled checks remain informational and excluded. Each text, surface and focus check includes `standardMinimum`, `marginAboveStandard` and `marginAboveTarget`. The standard floor is4.5 for normal text and3 for surface/focus pairs; the target is the check's `minimum` (textMinimum can be higher). Signed margins are unrounded-ratio differences displayed to four decimals, not percentages or tolerance guarantees. Disabled standard/margins are null. Brand/link tools do not gain margins in this update. `focus.fillDiagnostics` compares the chosen ring with each enabled default/hover/active/selected fill: state, fill, ratio, declaredAdjacent, potentialConflict and informationalOnly. potentialConflict means the raw ratio is below3 **if those colors touch**. Undeclared comparisons are informational, excluded from totals; they do not assume layout or downgrade `passes-listed-pairs`. A warning identifies undeclared fills needing review. Add actual touching colors to focusAdjacentColors; verify a real offset/alternative indicator otherwise. Already-declared colors are measured once in focus.checks, not double-counted. Unresolved focus has an empty diagnostic list and still exports no focus token. `audit_palette` now returns `scope:"all-pairs"` and `duplicates:[{hex,indices}]` (normalized HEX, zero-based indices). Colors and ratio-1 duplicate pairs remain present. Its note explains raw-threshold decisions and that failing non-adjacent pairs do not contradict a separate adjacent-only generation target. The audit receives no prior target and does not claim to have verified one. ## Persisted brand-role targets (public v1.14.0; account v1.4.0) `build_brand_system` accepts optional numeric `textContrast` (4.5–7; use7 for normal-text AAA on the listed pairs) and `uiContrast` (3–4.5 for listed border/focus pairs). These names match revision options. This tool does not accept accessible, contrastMinimum, or a caller-supplied contrast report. Prior palette targets cannot be inferred from HEX. Example: `{"colors":["#123456","#FEDCBA"],"mode":"both","textContrast":7,"uiContrast":4.5}`. Palette colors, primary/accent and generated backgrounds stay fixed; supporting roles may move toward black or white. A successful explicit request returns `feasible:true`, `canSave:true` and a saveable `system` with `contrastTarget:{textMinimum:7,uiMinimum:4.5}`. The persisted object is only a constraint, not a trusted pass label. Save validation independently measures every listed role pair against it. Copy the complete system to save_palette. No database migration or automatic update of existing saved palettes occurs. The outer `contrastTarget` is the freshly measured report: source, scope listed-brand-role-pairs, targets, met/unmet status, counts and exact pair measurements. CSS/Tailwind include this report as a comment, including default-floor context on legacy systems. Pair fields include exact role colors and signed margins above the AA/UI floor and requested target. Comments can be stripped downstream; preserve the source JSON. Studio links, saved metadata and account reads retain the constraint. Studio previews/exports use it; rebuilding in Studio retains an existing target and refuses invalid results. **Infeasible request:** copper #A65F40 as primary cannot support7:1 button text while staying fixed. The builder returns `feasible:false`, `canSave:false`, measured pairings/failures and `system:null`, `css:null`, `tailwind:null`, `studioUrl:null`, `usageChecks:null`. It does not return a lower-target system as though the request succeeded. This is a bounded search result, not a proof that every alternative design is impossible. `usageChecks` remains separate: primary/accent used as links are tested against the system's text target (or legacy4.5). Link failures remain visible even when all role pairs pass; suggestions stay `applied:false` and never become saved roles or replace anchors. Brand and link pair checks report signed margins; no interface, geometry, CVD or physical-print certification. ### Revision and history contract - Omitted preview text/UI targets inherit the saved constraint; legacy systems use4.5/3. Partial requests preserve the omitted saved target. Normalized options still contain all four fields required at commit. - Successful raised revision targets are persisted. Legacy no-op/default-floor previews remain unchanged. Explicit builder defaults are also persisted when supplied. - `changes` remains a list of role changes. New `targetChange` separately carries before/after constraints (null before means legacy defaults). A target-only change can be `ready-to-save` with `changes:[]` and `canSave:true`; review it before committing. Lowering a persisted target changes the contract, not the colors. - Unchanged constraints and roles yield canSave:false. Infeasible revisions still yield no saveable system. Commit requires the exact current version, normalized options and previewHash; **refresh previews made before this release**, because their measured result/hash changed. - History adds machine type `contrast-target-changed`, with before/after values. Existing historical snapshots and publication snapshots are not rewritten. Save/revise remains private and never republishes automatically. - Existing v1 light/v2 dark-or-both system formats remain accepted. Optional contrastTarget is strictly validated; forged status fields, omitted numeric target members and failed role pairs are rejected. Legacy saved systems are not retroactively claimed to meet stronger targets. ## Full tonal-scale exports (public v1.15.0) `export_palette` accepts **2–12 colors** and matching optional names in all seven formats: CSS, SCSS, Tailwind, JSON, SVG, ASE and DTCG. Pass `generate_tonal_scale.colors.map(c => c.hex)` for all11stops; optionally prepend `original.hex` for12colors. Supply stop labels as names to preserve their meaning. Input order, exact HEX, custom names and duplicates are retained; export keys default to prefix-index, not the tonal generator's stop-number keys; useNames:true opts into safe supplied-name keys. The tonal tool's own CSS/Tailwind retains its original stop keys. Optional contrast context measures each consecutive exported pair: 10 checks for11colors,11 for12. A tonal ramp is not expected to make every neighboring shade suitable for text; failed targets remain failures and colors are never repaired. ASE retains its JSON sidecar; DTCG retains context in its extension. This is an export-only limit increase. Studio links, saving, brand systems and palette audits retain their existing10-color limit. Choose a meaningful subset there. Thirteen colors, mismatched labels and invalid HEX still reject. Existing2–10-color artifact content is unchanged. ## Text contrast cushion in new brand builds (public v1.16.0) `build_brand_system` now prefers an extra **0.3 contrast ratio** for listed text roles: 4.8 at the default 4.5 floor, or 7.3 for an explicit 7 target. This is a QRCO generation preference, not a new WCAG requirement. It adjusts only new `text`/`mutedText` roles, searching 255 steps toward black and white and choosing the least squared RGB movement among passing samples. Palette colors, primary/accent, surfaces, UI roles and maximum-contrast black/white on-colors stay fixed. Already sufficient text is unchanged. The top-level `textCushion` reports `minimum`, `preferredMinimum`, `additionalRatio:0.3`, `status:met|partial`, `shortfallCount`, exact pair colors, measured ratios, raw-derived `meetsPreferred`, signed margins and changes. An unavailable cushion is explicit, including fixed anchors whose best ink cannot reach it. A bounded search miss keeps the valid original text and reports a shortfall; it does not declare global impossibility. Actual requested-target failure still produces `feasible:false`, null system/exports and `textCushion:null`. The saved numeric target remains the user's requested floor, not the preferred cushion (`persistedAsConstraint:false`). Generated role colors survive save/link/export as usual; the derived cushion report is outside the saved system. Existing saved reads, account revisions and Studio rebuilds are unchanged and do not automatically apply this preference. Link usage remains separate. Raw threshold decisions are unchanged: 4.5004 really passes a 4.5 requirement, but has little margin. The extra margin is not a guarantee under screen brightness changes, glare, later color edits, transparency, printing or all viewing conditions. ## Requested contrast versus achieved result (public v1.17.0) Generation results with an AA/AAA request now lead with **contrastSummary** and **warnings**, before name/colors/metadata. This applies to single generation, API-generated sets and random draws selecting an accessible mode. The existing `metadata.accessibility` string is retained for compatibility and means only the requested level. Never interpret it as an achieved accessibility rating. `contrastSummary` reports requestedLevel, targetStatus (`met`/`unmet` at the requested ratio), standardStatus (`met`/`unmet` at the selected AA/AAA floor), scope `adjacent-pairs`, thresholds, pair/failure counts and failed pair indices. `legacyAccessibilityMeaning` is `requested-target-only`; `wholePaletteCertified` is always false. Exact measurements remain in metadata.contrastTarget. Warnings are structured `{code,message}` entries: `CONTRAST_STANDARD_UNMET` when any pair misses the selected AA/AAA floor; otherwise `CONTRAST_HEADROOM_UNMET` when the floor is met but the requested higher ratio is not. Successful targets have an empty warnings array and the same scope caveat. No automatic AAA-to-AA relabeling, recoloring, changed measurements or new certification. Unrelated generation modes get no added contrast claims. MCP generate_palette_set still does not accept accessible; this does not broaden its schema. Example: locked #777777/#777777 with accessible aaa retains that request but leads with targetStatus/standardStatus unmet and CONTRAST_STANDARD_UNMET. Locked #767676/#FFFFFF with aa and contrastMinimum4.8 yields standardStatus met, targetStatus unmet, CONTRAST_HEADROOM_UNMET. Raw 4.5422 passes4.5 but not4.8. Read these verdicts before presenting a palette as suitable for the requested use. ## Explore tags (public v1.18.0) search_explore matches a literal name phrase or all query words across names and optional curated tags, case/accent-insensitively. Exact full titles rank first, then explicit intent tags (an exact tag or all query words as complete tag words), then incidental title phrases, then mixed name/tag word matches; ties retain catalog order. Use concise terms such as sunset or warm coffee. No embeddings, inferred intent or automatic synonyms. Results include sanitized tags when available (at most24 strings, at most40characters each). Tags describe editorial directions, not accessibility certification. Exact HEX, source/color-count filtering and empty browsing remain unchanged. Existing entries without tags continue to match names. Catalog cache lasts one hour per server process; restart after a catalog deployment for immediate visibility. ## Structured Explore retrieval (public v1.19.0 / account v1.5.0) Search before generating. `search_explore` combines `query` (declared names/tags), `colorCount`, `source`, measured `temperature` (warm/cool/neutral/mixed), `lightness` (light/dark/mixed), `chroma` (muted/balanced/vivid), and `minTextContrast` (3–21, at least one actual swatch pair). For “warm editorial, six colors, one AA pair”, pass query editorial, temperature warm, colorCount6, minTextContrast4.5. Do not put all objective requirements into the name query. Existing query/source/size/HEX/pagination inputs remain valid. Results retain names/colors/source/StudioURL and add IDs/custom names when present, properties and evidence: exact best/qualifying foreground/background pairs, raw-derived passes, matching-pair counts, and an explicit scope caveat. No palette-wide accessibility label, automatic generation or constraint relaxation. Ranking is exact full title, explicit intent tags, incidental title phrase, mixed all-word match, then catalog order; measured requirements are filters, not aesthetic scores. Measurements are cached from the compact catalog, never inferred from swatch names. Temperature ignores colors with CIELCH chroma below12; remaining HSL hues in [0,90) or [330,360) count as warm. At least60% warm/cool determines those categories, no chromatic colors means neutral, otherwise mixed. Mean CIELAB L*>=70 is light,<=35dark, otherwise mixed; mean LCH chroma<25muted,>=55vivid, otherwise balanced. These are explicit QRCO color rules, not universal emotional/cultural meanings. Declared tags may disagree; use measured filters for objective requirements. The catalog lacks reliable material provenance and full batch QA reports, so retrieval does not invent those. Public `excludePalettes` accepts up to50explicit color arrays. `similarityThreshold` defaults8 (0–20). Exact reordered duplicates always excluded. Equal-size palettes use one-to-one CIE76 matching, every distance strictly below threshold, sharing the batch primitive. Different-size palettes require every color in both directions to have a neighbor below threshold (many-to-one coverage). This heuristic is not compare_palettes' CIEDE2000 mean and does not establish role similarity or perceptual identity. Evidence reports the reference count and whether comparison occurred. Account `search_explore_for_account` accepts the same filters with `excludeSimilarToSaved:true` by default and requires only palettes:read. It reads the authenticated owner's full saved library internally (up to1000), with no caller-supplied identity and no private colors/names in shared caches or results. `libraryComparison` reports requested/complete/count. Set false to bypass that read. Public MCP never acquires account access; use the account connection for automatic exclusion. Existing read grants suffice after refreshing discovery. `status:matches|no-matches` denotes a completed search. More than50000candidate/reference comparisons returns `status:refine-query`, code EXPLORE_TOO_BROAD, total:null, no partial list; narrow filters. A library beyond1000 returns EXPLORE_LIBRARY_LIMIT with no partial exclusion. Honest no-match means no generated fallback was supplied. Normal pagination is stable for a fixed corpus/library; later calls can see edits. Schema validation remains strict. No saved data, color engine, auth policy or catalog content was changed by this feature. ## Descriptive swatch names (public v1.19.1) New engine results and unnamed Studio swatches share deterministic, case-insensitive naming rules that account for lightness and saturation as well as hue (for example brown, pink, terracotta, and gray). These are QRCO descriptions, not standardized pigment names or cultural claims. Supplied names, stored Explore names, saved palettes, and HEX values are preserved. Palette titles may change on new generation. No tool/schema changes; account remains v1.5.0. ## Exact locks across materials (public v1.19.2) `lockedColors` preserves the supplied sRGB value at its index across digital, print, fabric, paint and neon in single, set and random generation. HEX spelling is normalized to uppercase; lowercase input is the same color. Both `pin` and `anchor` preserve exact locks; anchor also influences unlocked colors. Material styling applies to unlocked colors. If locks conflict with an AA/AAA target, preserve the locks and report unmet contrast. See the 1.29.0 exact-lock continuity section: variation/downstream locks are explicit; locks plus keepLocked:false conflict. ## Connection capabilities — public1.20.0 / account1.6.0 Call `get_capabilities {}` on the current connection before assuming an action is available. It reports registration-derived tools and schema limits; authenticated responses include granted scopes and actions requiring additional permission. MCP export formats remain available on the public connection (up to12 colors); Studio/save limits remain10. Read tools/list for full schemas. Account access is free. A separate account connection uses https://qrco.ca/connect#account-connection; connecting does not automatically approve save/revise/publish scopes. Existing connections need explicit additional authorization for missing scopes. No credentials, private identities, plan/checkout or invented usage/reset information is returned. Public discovery now lists18 tools; account discovery lists6–10 according to scopes. ## Descriptive naming — October 2026 Automatic sRGB swatch names use shared naming v2.0.2 with hue and tonal compatibility. Palette titles follow final displayed colors; names do not assert paint identity, neon materials or achieved contrast. Existing stored/custom names and catalogs are preserved. Raw HEX and measured reports remain authoritative. Public MCP release1.20.1. ## Expressive palette titles — public release 1.20.2 Automatic titles use expressive vocabulary v3.0.0 with targeted final-color safeguards. Swatch naming remains 2.0.2; exact colors, measurements and generation behavior are unchanged. Existing saved/custom/catalog titles are preserved. No Explore migration is included. ## Image extraction: a complete input example (v1.21.0) Call `extract_image_colors` with this arguments object. This 2×2 image has three opaque red pixels and one opaque blue pixel: ```json { "imageData": { "width": 2, "height": 2, "data": [255,0,0,255, 255,0,0,255, 255,0,0,255, 0,0,255,255] }, "colorCount": 5 } ``` `data` is a flat, row-major RGBA byte array (0–255), exactly width × height × 4 values, not a URL, file path or base64 string. Downscale locally to about 100 pixels on the longest side, preserving aspect ratio. Maximum 16,384 pixels / 65,536 numbers. The example returns two colors, 75% red and 25% blue, with QRCO names; colorCount is a maximum, not a request to invent colors. Percentages describe participating sampled pixels, not semantic importance or the original full-resolution image. Fully transparent pixels are ignored unless a background is supplied. Partial transparency requires an explicit top-level `"background":"#FFFFFF"` (or the intended surface) for compositing. An entirely transparent image without a background is an error. ## Named exports: explicit opt-in (available since v1.21.0) Call `export_palette`: ```json { "colors": ["#365570", "#BB1835"], "names": ["Slate Blue", "Crimson"], "format": "css", "prefix": "color", "useNames": true } ``` This produces `--color-slate-blue` and `--color-crimson`. SCSS uses `$color-slate-blue`; Tailwind already inserts `--color-`, so use `prefix:"brand"` for `--color-brand-slate-blue`. Names are slugified safely and slug collisions get numeric suffixes. Non-Latin-only slugs fall back to indices. Original labels remain in `tokens[].name`. Use generated `colors[].name` as the input names; canonicalName can repeat, and distinct names can still collapse to the same slug. Without useNames, numeric keys remain stable for existing consumers. SVG is a labeled swatch sheet, not a CSS variable stylesheet: supplied names appear as text (long labels may truncate); ASE stores display names. Changing a name-derived token's name changes its key. No saved palettes or imported application variables are renamed automatically. ## Adjust amounts and structured hints (v1.21.1) `get_capabilities` lists `adjust_color.operationInputs` by operation. Lighten/darken/saturate/desaturate require amount 0–100 percent. Mix requires secondColor and amount 0–1 (the second color's weight); for example: ```json {"color":"#000000","operation":"mix","secondColor":"#FFFFFF","amount":0.5,"colorSpace":"srgb-linear"} ``` Grayscale/invert reject amount; secondColor/colorSpace are mix-only. The shared schema ceiling is not the mix range. Read the operation-specific constraints before choosing values. The three actionable hints for missing/invalid mix inputs, partial transparency needing a background, and no visible pixels now use `isError:true` with identical JSON text and `structuredContent`: ```json {"error":{"code":"MIX_INPUT_REQUIRED","message":"mix requires secondColor and amount from 0 to 1 (weight of secondColor)."}} ``` Image codes are `IMAGE_BACKGROUND_REQUIRED` and `IMAGE_NO_VISIBLE_PIXELS`. This is not a claim that all MCP errors share this envelope: schema validation and other existing errors retain their current formats. Failed calls never return an empty success. ## Revision inputs: preview first, then copy the reviewed result Read `get_saved_palette` first. It must have metadata.brandSystem. Substitute its actual id and current saved-palette version in this `preview_brand_revision` example (the UUID below is illustrative): ```json { "id": "11111111-1111-4111-8111-111111111111", "version": 2, "options": { "addMissingMode": true, "textContrast": 4.5, "uiContrast": 3, "lockedRoles": [] } } ``` The controls belong **inside options**, not at the top level. Version 2 here means the saved palette's current revision, not the system format version. These are illustrative targets: read existing constraints and the user's intent instead of inadvertently lowering them. Only after reviewing a `canSave:true` result and receiving the user's instruction to save, call `save_brand_revision` (palettes:revise scope) with this constructed arguments object: ```js { id: preview.id, version: preview.version, options: preview.options, previewHash: preview.previewHash, requestId: crypto.randomUUID() } ``` Copy the returned normalized options whole, including defaults. This JavaScript illustrates construction, not a literal JSON payload. An unchanged/infeasible preview must not be committed. Reuse the same requestId and exact payload only for retries of that save. Previewing does not save; saving does not publish. ## Interactive palette workspace Call `present_palette` on the public connection with 2–10 HEX colors, an optional title and matching custom names. Compatible MCP Apps hosts display QRCO’s own Generate button, color locks, Brand/Balance tabs and lead-share controls. Supply a full validated brand system for System view. Proportions affect the presentation, not the source colors. Generation and presentation do not save or publish. Refresh/rescan the public connector after updates and request a fresh presentation. Native mounting and generation persistence have been user-confirmed in ChatGPT. A text response alone does not prove UI rendering; look for QRCO’s actual controls. Hosts without embedded UI receive structured data and a Studio link. Artwork and file-download support depends on the host; ASE/PNG download acceptance in ChatGPT is still being checked. `export_palette` independently returns all seven supported formats as content. ## Watch a saved palette The authenticated account endpoint supports `palette.revised` for a specific authorized palette. In a supported ChatGPT Work environment, ask to watch that palette and explain changes using the exact event version from history. A watch does not grant write/publish permission. The client supplies and manages the callback and watch; user-visible output and device notifications depend on that client. Events use exact protocol `2026-07-28`: `server/discover`, `events/list`, `events/subscribe`, and `events/unsubscribe`, on the same account endpoint. They are protocol methods, not extra ordinary tools. Legacy MCP clients keep their existing tool behavior. Webhook delivery is at least once; deduplicate by event ID. No replay is offered. Subscription expiry is separate from OAuth grant expiry. See [OpenAI Events setup](https://developers.openai.com/plugins/build/mcp-events) for supported clients and callback requirements. ## Named recipe presets — 1.24.0 `generate_palette` and `generate_palette_set` accept two additional `inspiration` values: `ocean-sand` and `streetwear-blue-yellow`. Both require 4–7 colors; incompatible sizes return an error. The first is a cool, muted decorative ocean direction with one sand support. The second uses controlled blue/yellow supports and requests a measured 4.8:1 text pair. Neither is a whole-interface or aesthetic certification. Read `metadata.guidance.status`, `composition`, `finalChecks`, and `bestPair`. Final checks use the same temperature, mean chroma and pair-differentiation rules as collection measurement. Up to four bounded attempts are made; exact locks stay unchanged and may leave checks unmet. An unmet result is not proof no solution exists. `get_capabilities` derives the named-preset size limits and requirements from the runtime definitions. Existing surprise/default choices remain unchanged. ## Optional dashboard usage audit — 1.25.1 `build_brand_system` accepts `applicationUsage: {profile: "dashboard", surfaces: ["surface"]}`. Surfaces default to `surface`; unique `background` and/or `surface` roles are accepted. Optional `applicationUsageChecks` measures primary button fills and accent chart marks against those surfaces at the system UI target. Results remain separate from brand feasibility and normal-link checks. Failed fills can include an unapplied review of the existing border color; a passing outline pair never erases the fill failure or certifies geometry. No palette, roles, exports, Studio URL or saved data change. Without the option the response is unchanged; infeasible builds return a null requested report. ## Public MCP1.27.0 — guided batch selection Guided multi-option generation keeps its previous set as a fallback and evaluates a bounded additional pool. metadata.batchSelection reports exact one-to-one unordered CIEDE2000 and positional distances, actual pass count and fixed/unchanged/improved outcomes. Reordering the same swatches is not novelty. Exact locks, declared requirements, within-palette separation, non-tonal AA opportunities and title-collision guards take precedence. Palette order is not changed within an option; the set retains its existing score sort. Pool size excludes legacy retries and each option's internal candidates. Single-option and explicit modes retain their existing generation paths. Role recommendations distinguish colorful accents from quiet/light neutral supports; accentIndex may be null. These are recommendations, never assigned UI roles or accessibility certification. get_capabilities returns serverVersion and installed engineVersions on the public endpoint. This release does not add a schema fingerprint or change account discovery/authentication. The selection is bounded and heuristic, not a promise of objective beauty, global uniqueness, every-pair contrast or new art direction. More candidates consume CPU. Existing requests can remain unmet; preserve the measured failure and all exact locks. No tool arguments or tool count change. ## Public MCP 1.28.0 — Streetwear composition options Streetwear blue/yellow multi-option sets screen four additional generated composition plans after the regular batch selection. Only plans meeting final recipe checks and dark/light goals enter selection. Exact locks and material transformations remain authoritative. metadata.batchSelection records precedingSelection, structuralCandidates, eligibleStructuralCandidates and structuralScreening; declined plans do not replace the fallback. Existing failures stay visible. Public engineVersions.paletteCompositionLayouts identifies the installed policy. Single-option and other modes remain unchanged. No new tools or inputs, title grammar changes, saved palette edits or universal novelty/accessibility claims. ## Exact-lock continuity — public 1.29.0 / account 1.8.0 Refresh client discovery after installing this release. An exact lock is `{ "index": 0, "hex": "#C1A5A9" }`: zero-based palette position plus exact opaque sRGB bytes. Case and three-digit shorthand may normalize; no colour-space conversion is allowed to alter the locked bytes. Duplicate, out-of-range and conflicting declarations fail rather than being dropped. No state is inferred from a list of HEX values: explicitly carry locks between calls. ```json {"colors":["#C1A5A9","#292321","#F3EBDD"],"names":["Brand rose","Ink","Paper"],"lockedColors":[{"index":0,"hex":"#C1A5A9"}],"count":3,"material":"fabric"} ``` Pass this to `vary_palette` for Soft, Bold and Muted alternatives. Optional `names` remain associated with their original indices. Exact locks override material transformations and the soft `referenceColors` influence. With supplied colors, each lock must already match its index; use a deliberate edit to change a colour before locking it. A lock does not itself create a text-contrast requirement: pass `accessible` and optional `contrastMinimum` explicitly to re-evaluate adjacent pairs. If locked values prevent the target, they stay unchanged, the target stays `unmet`, and recommendations explain the remaining choices. Neither the tool nor the caller should silently unlock or lower the target. - Generation/set/random expose the shared declaration in `metadata.constraints`; variation, brand, presentation/export and saved current reads expose `constraints`. Its version1 semantics are `exact-srgb-at-index` and `lockedColors` is the transferable argument value. A Studio working preview exposes its current locks separately as well. - `build_brand_system`, `present_palette`, `create_studio_link` and `export_palette` accept optional `lockedColors`. Supplied colours/order and chosen primary/accent are retained. Generated brand builds combine explicit locks with `brandColor` at index0; contradictory declarations fail. Derived text/background/support roles have their own identities and constraints. Use revision `options.lockedRoles` for supporting semantic roles; palette locks do not pin every role derived from the same seed HEX. - `audit_palettes` / `compare_palettes` accept locks inside each palette item. Comparison's `differenceFromBaseline.baselineLockedPositions` checks baseline positions even for unordered matching. A colour present at another index does not preserve an index lock. Comparison is read-only and never repairs input. - Account `save_palette` accepts the same declaration, persists existing `metadata.locks` booleans, and current `get_saved_palette` returns the derived contract. Exact history responses expose that snapshot’s constraints outside the unchanged palette snapshot. Revision previews retain those constraints and export comments. Save identity includes flags; an unlocked and locked copy are distinct. Existing version/preview/retry/permission rules still apply. Save only when the user requests it. - Studio links restore lock flags and names; invalid/conflicting links or corrupt saved flags are refused before replacing the editor. Regeneration/variation results that violate locks are refused. Device and account reopen retain flags; reordering moves the colour, name and lock together as an explicit structural edit. Resizing refuses removal of locked trailing colours and keeps existing locks where indices remain. - JSON and SVG exports include lock metadata; CSS/SCSS/Tailwind include comments. DTCG includes QRCO extensions; ASE's report sidecar retains the declaration while its binary preserves swatch values/order. Brand CSS/Tailwind, including saved reads, revision previews and embedded System downloads, also carry lock comments. Embedded SVG artwork includes metadata; locked embedded PNG downloads include a companion JSON file with exact colors, names and locks. The PNG bytes and plain HEX alone remain visual/value-only exports. Keep a structured original: another application may ignore/remove metadata and cannot be assumed to enforce a QRCO lock. Legacy REST variation calls can still use `locked`, but supplied nonempty locks are now exact even without `keepLocked:true`. Combining locks with `keepLocked:false` is an explicit conflict: omit locks only after the user chooses to release them. The anonymous legacy `/api/palettes/saved` disk route refuses lock metadata it cannot retain; use Studio/account saving instead. Invalid declarations are never silently filtered. ## Explicit intent and application guidance (same release) Default Explore search remains literal name/tag search. Opt into a small controlled vocabulary with `search_explore({"query":"bakery","intentExpansion":"related"})` or the account equivalent. Known single nouns such as bakery/hoodie expand to listed related themes (for example cafe/coffee or streetwear/fashion/urban). `intentInterpretation` lists the rule; `queryMatch` distinguishes literal from related-theme results. Literal results rank first. Unknown briefs stay literal. Size, measured filters, saved-library exclusions, comparison limits and honest no-match behaviour remain unchanged. A related theme is a lead to inspect, not proof of a perfect design match; catalogue coverage is still limited. Generation, set/random generation and variations accept optional `intendedUse`: `apparel`, `branding`, `interiors` or `dashboard`. This currently adds `usageGuidance` with missing context, measured best text-pair opportunity and plain recommendations. It does **not** retune the recipe, alter colours, claim visual/physical/cultural suitability or inherit targets. Apparel requests need substrate/process/vendor proofs; interiors need samples/lighting; branding needs chosen roles and applications; dashboards need explicit text/control/chart/state/CVD checks. Application-driven composition/ranking and creative-fit acceptance remain separate future work. Saved-account and historical responses retain lock declarations outside unchanged snapshots. Brand/revision/System code exports include lock comments. Embedded locked PNG downloads include a companion JSON declaration; other apps may ignore it. MCP Apps resource v1.3.1. No generated palette colours, saved records or cultural meanings are migrated by this release. ## Explicit apparel application context (public 1.30.0) Generation, palette sets, random palettes and variations accept optional `applicationContext` with `intendedUse: "apparel"`. Declare `garment` as exactly one of `{ "paletteIndex": 0 }` (counts in palette size) or `{ "hex": "#202631" }` (external substrate). Declare `printUses` with `paletteIndex` and `kind`: `small-text`, `large-text` or `decorative`. Optional text `minimum` must be 4.5–21 for small text or 3–21 for large text; null is invalid and decorative uses have no text minimum. Optional `process` is descriptive text (1–80 characters); `inkColorLimit` is 1–10 distinct declared print colors. Declare 1–30 uses. Indices must exist; an indexed garment cannot also be a print use. Only guided multi-option sets use this context to select from the existing bounded candidate pool. Exact locks and quality guards remain; no extra candidates are generated. Better declared use can reduce palette variety. Inspect `usageGuidance.applicationChecks` and `metadata.applicationSelection` for remaining failures, objectives and before/after distances. `baselineOrder` binds baseline pair indices; `finalOrder` binds final pair indices to returned options. Application improvement is separate from distance improvement. No context or intendedUse alone retains previous generation behavior. Single/random/explicit-mode/variation requests evaluate context without application selection. Context does not save or publish, propagate to Studio/exports, certify physical print results or change account permissions. ### Print guidance disclosures (public 1.30.1) Explicit apparel context now returns `printCoverage`: indexed garment, declared print indices and colors not declared for print. Not declared does not mean unused in the design. `inkUsage.declaredColorGroups` lists each counted HEX and its declared indices; repeated uses/identical HEX count once. A declared print matching the garment still counts. No underbase or physical ink separation is inferred. `decorativeVisibility` reports existing screen contrast for every declared decorative use and whether its HEX equals the garment. All are informational: no decorative minimum or pass/fail threshold, and no physical visibility guarantee. Review size, outlines, intended subtlety and the actual fabric proof. No colors, custom names, locks, measurements or selection behavior change. No-context requests retain their prior behavior. ### Print-plan advisory (public 1.30.2) For explicit apparel context, `usageGuidance.printAdvisory` leads with the declared ink count/limit and undeclared print indices/HEX. Include these disclosures prominently in the final explanation; undeclared does not mean unused. The first recommendation is `EXPLAIN_PRINT_SCOPE_FIRST`. Over-limit choices remain unapplied: review printer capacity, eligible wholly unlocked declared color groups, or explicit separate-artwork plans. No color, custom name, lock, declared use or limit is changed. Groups containing any locked declared index are not offered for unlocked-group review. A locked-color lower bound assumes all current declared print indices remain printed; it is not a claim every design is impossible. No feasible revision, underbase cost or physical print result is promised. Decorative contrast stays informational. Within-limit/no-limit cases receive no over-limit options. ### Export result compatibility (public 1.30.3) `export_palette` returns the same validated export object in `structuredContent` and legacy JSON text. Its declared output schema requires nonempty artifact content; incomplete output is an explicit error, not an empty success object. Inspect `isError` first. Exporter errors retain JSON text with a code/message and omit structuredContent to avoid validating error objects against the success schema in current SDK clients. Schema and shared preflight validation may return the existing nonempty text error; do not assume every error has the exporter envelope. Artifact encoders and bytes, original colors/names/order, exact indexed locks and contrast measurements are unchanged. `content` contains UTF-8 text or base64 ASE; decode ASE before saving and retain its JSON sidecar. This transport hardening does not add formats, save files/accounts, guarantee host downloads or establish that any past client-visible empty result had a server-side cause. ## Search modifiers — public 1.30.4 For warm sunset use `search_explore({"query":"sunset","temperature":"warm"})` or explicitly opt in to `{"query":"warm sunset","intentExpansion":"related"}`. Leading warm/cool, dark/light and muted/vivid words become disclosed measured filters. Default literal search remains unchanged; contradictory modifiers/explicit filters fail with EXPLORE_QUERY_CONFLICT. Unknown remaining words stay required. This bounded grammar does not infer an arbitrary brief or guarantee relevance. No constraint is silently relaxed and no result is generated as a substitute. ## MCP search collection 1.31.0 Default public/account search uses a shared 6,141-record source: all 6,093 historical records plus 48 curated versions in 12 families, at sizes 4–7. Cabin/cozy/evening and hoodie tags apply only to selected, assistant-reviewed briefs. Use query:"cozy cabin evening" or query:"hoodie", colorCount:6; an optional minTextContrast:4.5 still requires a real swatch pair. No-match stays honest. Recipe/measurement gates and exact anchors were preserved. Stored sizes are independently measured; assistant visual review is not owner approval, universal beauty or physical print certification. Web Explore, old page URLs and its 6,093-record catalogue are unchanged. MCP Studio links open exact supplied colors; generic Studio resizing is separate from stored-family retrieval. No account mutation or automatic palette publication tool is used.