Skip to content

Components V2

Discord’s Components V2 combines layout, content, accessory, and interactive component types for richer message UI. discord-mcp wraps it in 8 tools, a schema validator, a client-side preview renderer, and 5 templates for common shapes. This page explains how those pieces fit.

discord-mcp supports these roles and placements:

Role Supported types Placement contract
Layout Container, Section A Container holds up to 10 supported children and cannot nest another Container. A Section holds 1–3 TextDisplays plus one Button or Thumbnail accessory.
Content TextDisplay, MediaGallery, Separator TextDisplay, gallery, and separator nodes can appear at the message root or inside a Container.
Interactive ActionRow, Button, string/user/role/mentionable/channel selects A root or Container ActionRow holds either 1–5 Buttons or exactly one select. A Button can also be a Section accessory.
Accessory Thumbnail, Button Thumbnail is valid only as a Section accessory; Button style and credential fields must agree.

File components require a matching uploaded attachment. The current components_v2_send and components_v2_edit inputs do not accept attachment uploads, so the validator rejects File nodes instead of publishing a payload Discord cannot resolve.

Tool Idempotent What it does
components_v2_build_container yes Build a Container scaffold.
components_v2_build_section yes Build a Section scaffold with its required accessory.
components_v2_build_media_gallery yes Build a MediaGallery from URLs.
components_v2_validate yes Run the schema validator against a built tree.
components_v2_preview yes Render a client-side text preview of the tree.
components_v2_send no Send a message with a V2 component tree.
components_v2_edit no Edit an existing message’s V2 component tree (destructiveHint: false).
components_v2_send_from_template no Send from one of 5 pre-built templates.

There is no build_action_row tool. ActionRow nodes are passed in hand-written, as entries in the components array of components_v2_build_container (or of components_v2_send directly) - they have no dedicated builder.

The build_* tools are pure construction helpers - they don’t hit Discord; and produce JSON the agent can stitch together. A builder is not a substitute for validating the final composed tree: build_container accepts unknown child values, so invalid combinations can still be assembled. validate and preview are also Discord-free. Three tools make REST calls: send and send_from_template (POST /channels/:id/messages) and edit (PATCH /channels/:id/messages/:id).

See components-v2-announcement recipe for an end-to-end example.

Source: tools/components-v2/_lib/validator.ts

Enforces Discord’s V2 constraints before sending, so the agent sees a clear VALIDATION_FAILED error rather than Discord’s terse 400 response:

  • Max 40 components recursively in a single message tree; each Container can have at most 10 direct children.
  • Container ⊂ Section / MediaGallery / ActionRow - Containers cannot contain Containers.
  • ActionRow ⊂ Button / SelectMenu - only interactive children allowed.
  • ActionRow cardinality - one to five Buttons, or exactly one select; no button/select mixing.
  • Button style contracts - styles 1–4 require custom_id, style 5 requires url, and premium style 6 requires only sku_id plus optional disabled.
  • Top-level placement - Button, select, and Thumbnail nodes must use their permitted ActionRow or Section parent.
  • Interaction identity - every custom_id is unique across the complete message tree.
  • MediaGallery items: 1–10 entries, each with a URL.
  • Required fields: every Container needs at least one child; every Section needs one to three TextDisplay children and a Button or Thumbnail accessory.

Failure shape: ValidationError with an issues array, each entry pointing to the JSON path where the violation occurred (for example, components[0].components[3] for a nested issue).

Source: tools/components-v2/_lib/schema.ts

A single Zod tree describes the V2 model, and the validator adds recursive domain checks with precise paths. Always call components_v2_validate after assembling multiple builder results and before sending.

Source: tools/components-v2/_lib/preview.ts

Renders a text-only approximation of how the tree will look when Discord displays it: container borders as box-drawing characters, sections as indented blocks, action rows as [Button] [Select▼] stubs. Useful for the agent to sanity-check the layout before spending a send call.

components_v2_send_from_template resolves variables inside the shared approval middleware, so its first call returns the same bounded review and payload_hash contract. components_v2_preview remains useful when a human wants a rendered text approximation before starting that flow.

components_v2_send, components_v2_edit, and components_v2_send_from_template add a server-side approval boundary after validation. The first call returns a redacted payload plus a bounded structural review, payload_hash, and one-time approval_id with an expiry; execute only after arming the server with MCP_DRY_RUN=false and repeating the exact call with __confirm:true, that __confirm_hash, and __confirm_id. Changing even one payload value invalidates the approval. For template sends, the hash covers the fully interpolated component tree and target channel. The approval is consumed once before the handler runs.

discord-mcp ships 5 pre-built templates as MCP resources, addressable under discord://components-v2/templates/{name}:

Template Use case
announcement Announcement layout with interpolated title and body.
incident_status Operational incident and status update.
poll_results Poll outcome summary.
release_notes Release summary and highlights.
welcome_card Welcome message for a member or community.

Each template accepts variables (e.g. {{title}}, {{accent_color}}) interpolated at send time via tools/components-v2/_lib/interpolate.ts.

components_v2_send_from_template accepts a template name + a vars object and emits the fully-resolved tree.

The flag is a one-way switch per message: V2 mode disables content, embeds, and stickers in the same message. A V2 send is therefore mutually exclusive with the legacy embed path.

Concern File
Type definitions _lib/schema.ts
Validation _lib/validator.ts
Preview rendering _lib/preview.ts
Variable interpolation _lib/interpolate.ts
8 tool definitions tools/components-v2/