columns > column > * paragraph); below that `innerBlocks` is left open rather than unrolled * further, since refusing legitimately deeper trees would be worse than not * checking them. * * @since 1.4.0 * @var int */ public const MAX_DEPTH = 3; /** * The block spec schema. * * Descriptions are carried only at the top level. Repeating them down an * unrolled tree tripled the schema an MCP client is sent without telling it * anything new; the nested levels exist to be validated, not read. * * `name` is deliberately not required here. The serializer accepts the * `blockName` spelling as an alias, and classic content carries a null name, * so naming is enforced by {@see BlockSerializer::build_block()} — it sees * both spellings and names the block at fault. * * @param int $depth How many levels of innerBlocks to describe. * @param bool $described Whether to carry per-property descriptions. * * @return array The schema. * @since 1.4.0 */ public static function spec( int $depth = self::MAX_DEPTH, bool $described = true ): array { $describe = static function ( array $property, string $description ) use ( $described ): array { if ( $described ) { $property['description'] = $description; } return $property; }; return [ 'type' => 'object', 'properties' => [ 'name' => $describe( [ 'type' => [ 'string', 'null' ] ], 'Block type, e.g. core/paragraph. Null for classic (pre-block-editor) content, the spelling a view-* call returns for it.' ), 'attributes' => [ 'type' => 'object' ], 'innerBlocks' => $depth > 1 ? [ 'type' => 'array', 'items' => self::spec( $depth - 1, false ), ] : [ 'type' => 'array' ], 'plaintext' => $describe( [ 'type' => 'string' ], 'Text content, when the block\'s own text attribute is not set.' ), 'html' => $describe( [ 'type' => 'string' ], 'Raw HTML, for core/html and for preserving a block this server cannot regenerate.' ), 'blockName' => $describe( [ 'type' => 'string' ], 'Alias of `name`, the spelling the WordPress block parser uses.' ), 'attrs' => $describe( [ 'type' => 'object' ], 'Alias of `attributes`, the spelling the WordPress block parser uses.' ), 'path' => $describe( [ 'type' => 'array', 'items' => [ 'type' => 'integer', 'minimum' => 0, ], ], 'Ignored on write. Accepted so a block read from a view-* call can be sent straight back.' ), ], ]; } }