CmAdmin Configuration
All cm-admin global settings live in the initializer file (config/initializers/cm_admin.rb by default) and are exposed through the CmAdmin.configure block. The block is applied once every app initializer has run, so the file can have any name and the models it references can rely on your other initializers (Devise, constants, etc.). Changes to the block need a server restart, but model references in it are refreshed on every code reload in development.
# config/initializers/cm_admin.rb
CmAdmin.configure do |config|
config.layout = 'admin'
config.included_models = [User, Post]
config.project_name = 'My App'
# ...see sections below for every option
end
Quick Referenceâ
| Option | Type | Default | Description |
|---|---|---|---|
layout | String | 'admin' | Name of the Rails layout used to render cm-admin pages. |
included_models | Array<Class> | [] | Models registered with cm-admin (the ones that appear in the sidebar and routes). |
cm_admin_models | Array<CmAdmin::Model> | [] | Resolved CmAdmin::Model wrappers. Populated automatically â do not set manually. |
project_name | String | '' | Display name shown in the browser title and header. |
sidebar | Array | [] | Custom sidebar navigation entries. When empty, the sidebar is auto-generated from included_models. |
auth_method | Symbol | :otp | Authentication strategy. Valid values: :otp, :password, :custom. |
use_catalyst_otp | Boolean | false | Opt-in flag to register the gem's Devise strategy and model for OTP auth. |
user_class | String|Class | 'User' | The host app user model. Used by auth lookup, mentions, support tickets, and history. Accepts a class or the model name as a string. |
user_lookup | Proc | email/phone lookup | Lambda that resolves a user from an identifier (email or phone). Override for custom lookup logic. |
otp_delivery_channel | Symbol | :email | How OTPs are delivered. Valid values: :email, :phone, :phone_or_email. |
otp_delivery_provider | Symbol|Object | nil | Phone delivery provider. :gupshup or any object responding to #deliver. Required when channel is :phone or :phone_or_email. |
display_role_on_sidebar | Boolean | true | Whether to show the current user's role on the sidebar. |
display_date_format | String | 'M d, Y' | Flatpickr/JS date format used when rendering date fields. |
display_datetime_format | String | 'M d, Y, H:i K' | Flatpickr/JS datetime format used when rendering datetime fields. |
global_search_models | Array<Class> | [] | Models searchable from the global search bar. |
enable_tracking | Boolean | false | Enables Google Tag Manager / tracking snippets in the layout. |
enable_email_logging | Boolean | false | Logs every CmAdmin.send_email delivery to the cm_email_logs table. |
email_template_settings | Hash | { show_copyright: true, footer_content: nil } | Default email template options (copyright footer, custom footer content). |
mcp_enabled | Boolean | false | Mounts the built-in MCP server endpoint at <mount_path>/mcp. |
mcp_system_prompt | String|nil | nil | Custom system prompt sent to MCP AI clients during the initialize handshake. |
layout_css_files | Array<String> | [] | Extra host-app SCSS files compiled into the cm-admin CSS bundle. |
stylesheet_import_paths | Array<String> | [] | Extra directories the Sass compiler searches when resolving @import/@use in bundled SCSS. Useful for exposing stylesheet directories from Ruby gems. |
Layout & Brandingâ
layoutâ
Name of the Rails layout used to render cm-admin pages. Defaults to 'admin'.
config.layout = 'admin'
project_nameâ
Shown in the browser title and the cm-admin header. Commonly wired to your Rails project settings:
config.project_name = Rails.configuration.x.project_settings.name
sidebarâ
When provided, replaces the auto-generated sidebar with custom navigation entries. When left empty (default), cm-admin builds the sidebar from included_models, filtered by the current user's policies.
config.sidebar = [
{ label: 'Dashboard', path: '/dashboard', icon: 'fa-solid fa-gauge' },
{ label: 'Users', path: '/cm_admin/users', icon: 'fa-solid fa-users' }
]
display_role_on_sidebarâ
When true (default), the current user's role name is displayed on the sidebar. Set to false to hide it.
config.display_role_on_sidebar = false
Modelsâ
included_modelsâ
The list of ActiveRecord models that cm-admin should register. Each model must have a corresponding CmAdmin::<Model> concern with a cm_admin DSL block. Only models listed here appear in the sidebar, routes, and MCP tool discovery.
config.included_models = [User, Post, CmRole, ApiToken]
Generator-based features (scheduler, support tickets, etc.) will prompt you to add their models here.
cm_admin_modelsâ
This is a runtime-populated collection of CmAdmin::Model wrappers built from included_models. Do not set this manually â it is managed by cm-admin during boot.
global_search_modelsâ
Models that should be searchable from the global search bar in the header. The first three are surfaced as quick links in the search dropdown.
config.global_search_models = [User, Post]
Authenticationâ
auth_methodâ
Controls which login flow cm-admin renders. Valid values are :otp (default), :password, and :custom. Setting an invalid value raises ArgumentError.
config.auth_method = :password
:otpâ identifier-based OTP login. The user enters their email or phone and receives a one-time code.:passwordâ identifier + password login.:customâ host app provides its own authentication flow. The Devise strategy signs the user in directly viasuper(user).
The chosen method also toggles password-related UI (e.g. "forgot password" links are only shown for :password).
See OtpAndAuth for the full setup guide including migration from host-app controllers to the gem.
use_catalyst_otpâ
Opt-in flag that registers the gem's Devise strategy (CmAdminAuthenticatable) and model module. When false (default), the gem does not interfere with the host app's Devise setup. When true, the gem registers a Warden strategy and Devise module via ActiveSupport::Reloader.to_prepare so it runs after app initializers but before eager load.
config.use_catalyst_otp = true
user_classâ
The host app's user model. Defaults to 'User' and is lazily constantized, so it can be set before the model is loaded or after a Rails code reload. Pass either the class or its name as a string.
config.user_class = 'AdminUser'
# or
config.user_class = AdminUser
This value is used by the default user_lookup, the support-ticket @mentions model, the history page (whodunnit resolution), and any other cm-admin feature that needs to talk to the user table. Changing it avoids hardcoded ::User references.
user_lookupâ
A lambda that resolves a user from an identifier string (email or phone). The default lookup tries config.user_class.find_by(email:) first, then falls back to a LIKE match on phone_number using the trailing digits.
config.user_lookup = lambda { |identifier|
AdminUser.find_by(email: identifier) || AdminUser.find_by(phone_number: identifier)
}
The lambda is called by the Devise strategy and the OtpSessions concern. Override it to customize lookup logic (e.g. normalized phone formats).
otp_delivery_channelâ
Controls how OTP codes are delivered. Valid values: :email (default), :phone, :phone_or_email. Setting an invalid value raises ArgumentError.
config.otp_delivery_channel = :phone_or_email
:emailâ sends OTP via email only (usingCmAdmin::DeliveryProviders::Email).:phoneâ sends OTP via the configuredotp_delivery_provideronly.:phone_or_emailâ the login screen shows a toggle between phone and email login. A phone field (with country-code selector) is shown by default, with a "Use email instead" link to switch to an email field (and a "Use phone instead" link to switch back). OTP delivery follows the chosen identifier: phone numbers are sent via the phone provider only, emails via email only.
otp_delivery_providerâ
The provider used for phone-based OTP delivery. Accepts a Symbol (resolved to a class under CmAdmin::DeliveryProviders) or any object responding to #deliver(user:, otp:, template:).
# Using the built-in Gupshup provider
config.otp_delivery_provider = :gupshup
The gem ships with CmAdmin::DeliveryProviders::Gupshup for WhatsApp OTP delivery. Gupshup credentials are read from Rails.application.credentials[:gupshup] and should include :api_key, :source_number, and :templates (a hash mapping template names to Gupshup template IDs).
To use a custom provider, pass any object that responds to #deliver:
class MySmsProvider
def deliver(user:, otp:, template:)
# send SMS via your provider
end
end
config.otp_delivery_provider = MySmsProvider.new
Date & Time Formattingâ
display_date_format and display_datetime_formatâ
These are Flatpickr formatting strings passed to the JS date/datetime pickers used across index and form views.
config.display_date_format = 'M d, Y' # e.g. "Aug 6, 2026"
config.display_datetime_format = 'M d, Y, H:i K' # e.g. "Aug 6, 2026, 14:30 PM"
These only affect the picker UI. Use the
format:option on individualcolumn/fielddeclarations to override how stored values are rendered.
Emailâ
enable_email_loggingâ
When true, every email sent via CmAdmin.send_email is recorded in the cm_email_logs table with delivery status, recipients, failure reasons, and record association. Disabled by default.
config.enable_email_logging = true
To set up the required table:
rails g cm_admin:add_email_logs
rails db:migrate
See CmAdminEmail for the full email sending API.
email_template_settingsâ
Default options applied to every email template unless overridden per-call:
config.email_template_settings = {
show_copyright: true, # render the © footer
footer_content: 'Acme Inc.' # custom HTML/text shown above the copyright
}
Both keys can be overridden on individual CmAdmin.send_email calls via the email_template_settings: argument.
Trackingâ
enable_trackingâ
When true, cm-admin injects Google Tag Manager snippets into the layout head and body. Use this only in environments where GTM is configured.
config.enable_tracking = true
MCP Serverâ
cm-admin ships with a built-in Model Context Protocol server that exposes your admin-registered models to AI clients. See McpServer for the full setup guide.
mcp_enabledâ
When true, the MCP Rack endpoint is mounted at <cm_admin_engine_mount_path>/mcp and models with mcp_actions in their DSL block become available as list/show tools. Defaults to false.
config.mcp_enabled = true
mcp_system_promptâ
A custom system prompt sent to the AI client during the MCP initialize handshake. This is the right place to inject domain-specific knowledge, business semantics, and known limitations the AI should respect.
config.mcp_system_prompt = <<~PROMPT
You are reading records from the Acme Platform via MCP tools.
Data model notes:
- `assignee` is the current owner of a work item, not the original creator.
- `internal_feedback_rating` is unused; QA ratings live inside each work
item's history and cannot be bulk-aggregated through MCP tools.
Best practices:
- Use aggregate tools for counts/sums/averages instead of listing all rows.
- Use `fetch_options` to resolve select filter values before filtering.
PROMPT
When left nil (default), the server falls back to a generic one-line instruction. Keep the prompt focused on semantic guidance â avoid pasting the full schema, which the AI can already discover through list_models and tool schemas.
CSS Bundlingâ
layout_css_filesâ
Extra SCSS files from the host app (app/assets/stylesheets/) that should be compiled into the cm-admin CSS bundle. Each entry is a filename relative to app/assets/stylesheets/.
config.layout_css_files = ['application.scss', 'theme_overrides.scss']
This is used by the cm-admin CSS bundler (CmAdminCssBundler) when building the Tailwind/Sass output. Each file is compiled to app/assets/builds/<name>.css.
stylesheet_import_pathsâ
Extra directories the Sass compiler should search when resolving @import/@use directives in the bundled stylesheets. This is commonly used to expose stylesheet directories from Ruby gems so SCSS can find their partials.
config.stylesheet_import_paths = [
"#{Gem.loaded_specs['bootstrap'].full_gem_path}/assets/stylesheets",
"#{Gem.loaded_specs['select2-rails'].full_gem_path}/vendor/assets/stylesheets",
"#{Gem.loaded_specs['intl-tel-input-rails'].full_gem_path}/app/assets/stylesheets"
]
These paths are passed to the Sass compiler as --load-path=<path> flags and are combined with the default layout_css_files build.
Deploymentâ
To suppress Hatchbox deployment Slack notifications, set DEPLOYMENT_ALERTS to false in your Hatchbox environment variables â see HatchboxScripts.