Command and config reference
This page lists every console command the package ships, every configuration key in config/cms.php with its default, the cookie consent settings, and the blocks in the block picker. It is written for a developer looking up a specific command, key or block key. The full reference, with a description of every key, is docs/reference.md in the package.
Commands
The package ships 17 cms:* commands, registered in FilamentCmsServiceProvider from the classes in src/Console/. Two further commands, mcp:start and mcp:inspector, come from laravel/mcp. A missing required argument is prompted for in an interactive terminal; under the scheduler, in CI or with --no-interaction, no command waits for input.
| Command | Effect |
|---|---|
| cms:install | Publishes assets, runs migrations, registers the panel, and offers to create the first administrator, set the site name and choose a skin. --panel= installs into an existing panel by id |
| cms:license {key?} [--show] | Shows the recorded license key, or records a new one |
| cms:make-block {Name} [--interactive] | Generates a block class and its Blade view. --interactive asks what the block holds and scaffolds its fields |
| cms:make-block {Name} --livewire | Generates a single-file Livewire block: one Blade file holding the schema, validation and markup, with no class |
| cms:make-content-type {Name} [--tree] | Generates a content type class. --tree makes content of that type nest under a parent |
| cms:make-layout {Name} [--regions=main] | Generates a page layout in the published layouts directory, where discovery finds it. --regions= takes a comma-separated list |
| cms:make-skin {Name} [--partial] | Generates a skin stylesheet, and with --partial a Blade partial for runtime behavior |
| cms:mcp-token {user} [--name=mcp] [--revoke] | Issues an API token for the MCP HTTP endpoint. --revoke deletes the user's existing tokens with that name first |
| cms:publish-scheduled | Publishes and unpublishes content whose scheduled time has arrived. Registered automatically, every minute |
| cms:prune-versions [--keep=] [--force] | Deletes old content versions, always keeping the published and draft versions |
| cms:prune-submissions [--days=] [--dry-run] [--force] | Deletes form submissions past their retention period. Registered automatically, daily, when forms.retention_days is set |
| cms:cache-clear | Purges every cached CMS page |
| cms:reindex [--all] | Rebuilds the search text stored on versions; --all rebuilds every version, not only those with none |
| cms:layout-areas | Creates a content area for every shared region the layouts declare; idempotent |
| cms:locales:backfill [--locale=] [--dry-run] | Assigns the default locale to content written before locales were enabled |
| cms:sanitize-svg [--dry-run] | Removes scripts from SVG files stored before uploads were sanitized |
| cms:demo [--fresh] [--skin=] [--force] | Seeds a demonstration site: seven pages, two snippets, a footer menu, a form and site settings. --fresh permanently deletes what a previous run created |
| cms:placeholders {count=6} [--width=1600] [--height=900] [--style=stripes] | Draws placeholder images with GD and imports them into the media library. --style= is stripes or flat. Requires ext-gd |
| mcp:start cms | Runs the MCP server over stdio; invoked by MCP clients |
| mcp:inspector cms | Opens the MCP Inspector to debug a connection |
config/cms.php
The config file is published by php artisan vendor:publish --tag=cms-config, or by cms:install. Publishing is optional: the package's own file is merged into the application's configuration whether or not a copy is published.
A published config file does not receive keys added in later releases. mergeConfigFrom merges only the top level of the array, so a published file's blocks array replaces the package's blocks array entirely, and a key the package adds to blocks in a later release is absent from the merged result. The mcp group is the one exception since 0.78.4. See Known traps. After an upgrade, compare the published file with the keys below, which are taken from config/cms.php in the package.
'license' => [
'key' => env('CMS_LICENSE_KEY'),
'repository' => 'packages.mcgstrategic.dev', // CMS_LICENSE_REPOSITORY
],
'panel' => ['id' => 'cms', 'path' => 'cms', 'brand' => 'CMS', 'default_parent' => 'homepage'],
'route' => ['prefix' => '', 'middleware' => ['web'], 'frontend' => true], // CMS_FRONTEND_ENABLED
// Read-only delivery API. Off by default; see The delivery API.
'api' => [
'enabled' => false, // CMS_API_ENABLED
'prefix' => 'api/cms/v1', // CMS_API_PREFIX
'middleware' => ['api', 'auth:sanctum'],
'draft_guard' => 'sanctum', // CMS_API_DRAFT_GUARD
'origins' => [], // CMS_API_ORIGINS, comma-separated
],
'webhooks' => ['enabled' => true, 'urls' => [], 'secret' => env('CMS_WEBHOOK_SECRET'), 'timeout' => 5],
'notifications' => ['enabled' => false, 'timeout' => 5], // CMS_NOTIFICATIONS_ENABLED
'cache' => ['enabled' => true, 'store' => null, 'ttl' => null, 'allowed_query_params' => []],
'media' => ['disk' => 'public', 'max_upload_kb' => 10240, 'sanitize_svg' => true],
'dashboard' => ['widgets' => [/* four package widgets */], 'columns' => 2],
'vite' => ['entries' => ['resources/css/app.css']],
// Editing a block from the public site, for an editor signed in to the panel.
'inline_editing' => true,
'inline_editing_vite_entry' => null, // null uses the panel's own stylesheet
'navigation' => [
'types' => ['page'],
'root' => 'auto', // 'top', or a path or content id
'derived' => ['max_columns' => 6, 'max_links_per_column' => 6, 'max_dropdown_links' => 12],
],
'menus' => ['enabled' => true],
'theme' => [
'skin' => null, // press, signal, marquee, saas, arcade, market, or null
'skins' => [], // extra skins beyond the six that ship, key => label
'skin_fonts' => [], // Bunny Fonts families for an application skin, key => list
'mode' => null, 'toggle' => true, 'fonts' => true,
'menus' => ['header' => null, 'footer' => null],
'home_link' => true, // true or absent: the translated Home; null or '': none; a string: that label
],
'layouts' => ['discover' => true, 'register' => []],
'preview' => ['ttl_minutes' => 60],
'versions' => ['keep' => null],
'draft_view' => [
'enabled' => true, 'reload' => true, 'polling' => true, 'poll_seconds' => 2,
'auth_check_seconds' => 60, // session check for an open editor tab; 0 disables it
],
'snippets' => ['enabled' => true, 'path_prefix' => '/_snippets'],
'layout_areas' => ['enabled' => true, 'path_prefix' => '/_layout-areas'],
'forms' => [
'enabled' => true,
'upload_disk' => 'local', // must be a private disk
'upload_directory' => 'cms/form-uploads',
'max_upload_kb' => 10240,
'turnstile' => [
'site_key' => env('CMS_TURNSTILE_SITE_KEY'),
'secret_key' => env('CMS_TURNSTILE_SECRET_KEY'),
'skip_locally' => true,
],
'throttle' => ['per_minute' => 5, 'per_hour' => 20],
'min_seconds' => 2,
'retention_days' => null,
'mail_from' => null,
],
'tags' => ['enabled' => true, 'prefix' => 'tags', 'per_page' => 20],
'sitemap' => ['enabled' => true, 'robots' => true, 'ttl' => 3600, 'disallow' => ['/cms-preview']],
'feed' => ['enabled' => true, 'path' => '/feed.xml', 'source' => null, 'limit' => 20, 'ttl' => 3600],
'structured_data' => ['enabled' => true],
'consent' => [
'enabled' => true,
'privacy_url' => '/privacy',
'analytics_category' => 'analytics',
'categories' => [
['key' => 'functional', 'label' => 'Functional', 'required' => true, 'default' => true],
['key' => 'analytics', 'label' => 'Analytics', 'required' => false, 'default' => false],
],
],
'permissions' => ['enabled' => false, 'super_admin' => null],
// The map block's address lookup (the Find button). The map itself has no settings.
'maps' => [
'geocoder' => [
'enabled' => true,
'endpoint' => 'https://nominatim.openstreetmap.org/search',
'user_agent' => null, // null builds one from app.name and app.url
'timeout' => 5,
'cache_days' => 30,
],
],
'content_types' => [
'discover' => ['App\\Cms\\ContentTypes' => null],
'register' => [],
],
'blocks' => [
'discover' => ['App\\Cms\\Blocks' => null],
// Single-file Livewire blocks, keyed by component-name prefix. null
// derives the directory under resources/views/livewire. [] switches it off.
'livewire' => ['blocks' => null],
'register' => [],
'disable' => [],
'override' => [],
'raw_html_ability' => null,
'embed' => ['allowed_hosts' => []],
],
'mcp' => [
'enabled' => true,
'handle' => 'cms',
'name' => env('CMS_MCP_NAME'), // null reports "Filament CMS: " followed by app.name
'actor' => null,
'local' => ['trusted' => true],
'web' => ['enabled' => false, 'path' => '/mcp/cms', 'middleware' => []],
'blocks' => ['denied' => [], 'gated' => ['html']],
// webhooks and notifications are always denied in addition to this list
'settings'=> ['denied' => ['analytics']],
],
// Multilingual content. Off by default; see Multilingual sites.
'scoping' => [
'sites_enabled' => false,
'locales_enabled' => false, // CMS_LOCALES_ENABLED
'default_locale' => 'en', // CMS_DEFAULT_LOCALE
'locales' => [], // CMS_LOCALES, comma-separated
],
// Run php artisan cms:locales:backfill after enabling locales.
'theme' => [
// ...
// A colophon line at the end of the footer, for a license credit that
// requires attribution. Rendered unescaped, so it can link to the license.
// Stored in config, not the database, so no editor can change it.
'footer_note' => null,
],
Cookie consent
The cookie consent banner is enabled by default (consent.enabled is true). The earlier default was false, on the reasoning that a site setting no cookies beyond the session should not ask permission for them. With that default, an installer had to know to enable the banner, and an analytics tag pasted into Site Settings by someone who had not read the config file ran for every visitor without asking.
The banner is configured in the admin panel under Site Settings β Cookie consent and in config/cms.php. The stored setting takes precedence over config, which is the same resolution order as the skin. Config is the deployed default that travels with a release; the stored value is an editor's override. The person who pastes an analytics tag into the Analytics section, one panel above, also has to declare a category for it, and can do so in the panel without opening a PHP file.
The resolution has two specific rules:
- A stored
nullmeans "not set in Site Settings", not "off". An installation that ran the migration adding the column and never opened the page keeps the banner configuration that config gives it. - An empty stored category list falls back to config. A list cleared in the panel is usually one nobody filled in, and a banner with no categories would offer the visitor no choices.
Set enabled to false for a site that sets no cookies and loads no tags. The banner is then not rendered at all.
A category's key is the name a visitor's stored answer is filed under. consent.analytics_category names the category whose answer releases the analytics scripts stored in Site settings. It defaults to analytics; null assigns the scripts to no category, so they never run. It is the only category the package acts on itself. Every other category is reported through the cookie-consent-updated event for the application's own code to act on. Before this key existed, the category was the fixed string analytics: renaming the category in Site settings left the scripts waiting on a key that no answer carried, and analytics never ran, with no error. Site settings now shows a warning when analytics scripts are present and no category has the configured key.
Renaming a category key discards every answer already given under the old key, so the field is validated instead of accepting free text. A category with required set is always on and is rendered as a disabled checkbox.
Scripts are sent to the browser as data and injected only after the visitor allows their category. Printing the script tags and removing them later would not prevent them from running, because a script tag runs as soon as it is rendered.
Answers are stored in localStorage under the key cookie_consent, as a map of category key to boolean, and every change dispatches a cookie-consent-updated window event carrying the map. Both match the mcgstrategic/cookie-consent package, so a site moving from that package keeps the answers its visitors already gave.
The block library
Forty-four blocks appear in the block picker with the default configuration, listed by category below. snippet and form are registered only when cms.snippets.enabled and cms.forms.enabled are true, which is the default for both. A forty-fifth block, hero_carousel, is registered but withdrawn from the picker.
page_heading renders a page's h1 and the sentence under it, and is limited to one per version because a document has one h1. pricing_table takes a repeater of tiers with no upper limit, stores prices as written so that values such as "Free" and "Talk to us" can be entered, and takes features one line at a time.
| Category | Keys |
|---|---|
| Text | rich_text, code, callout, steps, definition_list, citations, link_list, content_section, testimonial |
| Media | image, image_grid, logo_wall, carousel, video, audio, embed, map, file_download |
| Layout | hero, page_heading, section, columns, cards, tabs, spacer, breadcrumbs, table_of_contents, series_nav, navigation, snippet |
| Interactive | cta, button_group, accordion, search, subscribe, form |
| Data | stats, pricing_table, table, page_list, tag_list, related, comparison, html |
hero_carousel is registered but withdrawn from the picker. The hero block now takes slides, so one block covers both uses. The hero_carousel key is kept registered so that pages published while it was in the picker continue to render.