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.
Component DSL limits
Exposed in thelimits field of list_propal_components:
| Limit | Value | Meaning |
|---|---|---|
MAX_SECTIONS | 10 | Sections per single append call |
MAX_DEPTH | 6 | Max nesting depth (columns inside columns…) |
MAX_NODES | 200 | Total nodes per tree (assembler-side count) |
MAX_BLOCKS_PER_SECTION | 50 | Direct children of a single section |
MAX_BLOCKS_PER_COLUMN | 30 | Children inside one column of a columns block |
MAX_COLUMNS | 4 | columns.cols upper bound |
tree exceeds MAX_NODES (200).
Structural rules
Enforced both by Zod and a runtime validator (so you get a clear message, not a generic Zod failure):- Root must be a
sectionorsection[] - A
sectioncannot contain anothersection columns.colsmust equalcolumns.lengthembed.srcmust match the provider’s host allowlist (HTTPS only)image/icon/videocannot have asrc(placeholders only)
Theme validation
themes.section_styles[].idis optional, but if present must be 1-32 chars matching^[a-zA-Z][a-zA-Z0-9_-]*$.- Reserved ids (rejected):
"default"and any digit-only string. - Ids must be unique within a single theme — duplicates →
BAD_REQUEST. apply_theme_to_proposalchecks the theme belongs to the org; otherwise →NOT_FOUND.
Error envelope
MCP errors return as the standard MCP error result:Validation
Invalid component tree: <path>: <reason>section cannot be nested inside another sectionembed.src host does not match provider "<provider>"tree exceeds MAX_NODES (200)
Authorization
Missing required scope: mcp:useProposal not found— usually means the ID doesn’t belong to the user’s current organization (cross-org access is blocked).
Concurrency
Proposal changed too quickly. Please retry the append.— 3 consecutive optimistic-update conflicts. Refetch viaget_proposal_treeand retry.
Out-of-bounds
index <N> is out of bounds (must be 0..<max>)order length <N> does not match document length <M>order contains duplicate index <N>
Theme reference
section.section_style "<name>" is not a known slot id in the applied theme. Available ids: "soft" (1), "deep" (2), "3".
Rate limiting
The MCP server enforces the same per-organization rate limits as the REST API. Bursts are smoothed; sustained throughput is capped. If you exceed the limit, the response includes aRetry-After hint and the
agent should back off.
Concurrency model recap
Every mutation tool uses optimistic concurrency onproposals.updated_at:
- Snapshot the doc +
updated_at. - Apply the mutation locally.
UPDATE … WHERE updated_at = <snapshot>— guard.- On conflict (someone else wrote): refresh + retry, up to 3 times.
- On 3 consecutive conflicts: surface the error to the agent.
get_proposal_tree, get_proposal_resolved_style) don’t take a
lock — they reflect the current row.
Best practices
Refetch between mutations
Indices shift after
insert / update (1 → N) / delete / reorder.
find_and_replace_in_proposal is the exception — it doesn’t change the
structure.Validate before applying when chaining
validate_propal_component_tree lets you check a tree without writing.
Useful for dry-runs and error explanation to the user.Use named section_style ids
section_style: "deep" survives reordering of the theme’s slots. "2"
silently shifts when slots are reordered.Honor WCAG warnings
generate_theme_from_palette returns warnings — surface them to the
user before persisting if contrasts are weak.