The MCP exposes the same theming model the editor uses — palette + ~3 section ambiences + typography + radii — through a layered set of tools that match the agent’s intent.Documentation Index
Fetch the complete documentation index at: https://docs.propal.io/llms.txt
Use this file to discover all available pages before exploring further.
The three layers
| Layer | What it controls | Where it’s stored |
|---|---|---|
| Theme | Page palette, fonts, radius, plus a list of “section ambiences” | themes table (per org) |
| Section | Which ambience this section adopts (section_style), plus optional background image | data-* attrs on the TipTap section node |
| Page | Fallback when a section opts out (section_style: 'default') | The theme’s page-level tokens |
theme_id. If absent, it falls back to a
legacy proposal_style jsonb on the proposal row.
Three modes of creating a theme
1. Preset (assisted)
Pick from the catalog and snapshot it. Lowest effort — the catalog has ~25 curated palettes (matcha, ocean, ink, midnight, …) covering most use cases.2. Smart palette (generated)
Provide a mini-palette, the server generates a full WCAG-compliant theme viachroma-js — including accent solid/subtle variants, badges, and 3 section
ambiences automatically derived from the section backgrounds.
3. Advanced (full control)
When the agent needs to set every token explicitly — e.g. matching a brand guideline that doesn’t fit smart-mode generation — usecreate_theme with
the typed colors and section_styles arrays.
update_theme (full PATCH semantics) or with the more
targeted update_theme_section_style:
Named slot ids — the recommended way
Each entry insection_styles[] accepts an optional id (1-32 chars,
[a-zA-Z][a-zA-Z0-9_-]*). Reserved values: "default" and any digit-only
string. Ids must be unique per theme.
When sections reference a slot by id (section_style: "deep"), the server
resolves it to the positional index before assembly. Survives reordering:
if you later reorganize section_styles[], sections that use named refs keep
pointing at the same ambience. Sections that use positional refs ("1",
"2") silently shift.
The reverse mapping is also done in get_proposal_tree: positional refs are
converted back to ids in the response when ids exist.
Apply / detach
snapshot_style: false, the proposal falls back to its legacy
proposal_style (or defaults if absent) — useful when you want to “reset” a
proposal to a different visual baseline.
Read the resolved style
- If
proposal.theme_idis set → build the style from the theme row. - Else if
proposal.proposal_styleis set → parse it. - Else → defaults from
@propal/core/theming.
Per-section overrides
Sometimes you want one section to break the theme — e.g. a hero with a background image and a different ambience. Useset_proposal_section_attrs:
Tips
- Generate first, refine later.
generate_theme_from_paletteproduces WCAG-safe results in seconds. Useupdate_theme_section_styleto tweak individual slots if needed. - Always name your slots. It’s a 5-character habit (
id: "deep") that saves debugging when you reorder slots later. - Check the warnings.
generate_theme_from_palettereturns WCAG warnings inline — surface them to the user before persisting if contrasts are weak. - Don’t mix advanced and smart in one go. If you start with
generate_theme_from_paletteand want to tweak, preferupdate_theme_section_stylefor surgical changes; fullupdate_themePATCH still works but rewrites are noisier.