CLI Commands Since 3.5.0
The tcms CLI tool is the command-line interface to Total CMS. It exposes core CMS services as composable terminal commands, designed for both AI coding agents and human developers.
Running the CLI
Section titled “Running the CLI”php resources/bin/tcms <command> [options] [arguments]All commands support a --json flag that outputs valid JSON to stdout. This is the contract AI agents rely on — no decorative output, no progress bars, no color codes.
Global Options
Section titled “Global Options”| Option | Description |
|---|---|
--json | Output as JSON (for AI agent compatibility) |
-v | Verbose output |
-q | Quiet mode (errors only) |
-h, --help | Display help for a command |
-V, --version | Display version |
Site Information
Section titled “Site Information”Show site status, version, edition, license, collection count, and cache backend.
tcms infotcms info --jsonJSON output:
{ "version": "3.2.2", "build": "7f080a63", "edition": "pro", "license": { "valid": true, "trial": false, "trialDaysRemaining": null }, "domain": "example.com", "collections": { "total": 12 }, "schemas": { "reserved": 22, "custom": 4 }, "cache": { "backend": "apcu" }}Schema Commands
Section titled “Schema Commands”schema:list
Section titled “schema:list”List all schemas.
tcms schema:listtcms schema:list --customtcms schema:list --reservedtcms schema:list --category=Commercetcms schema:list --json| Option | Description |
|---|---|
--custom | Only show custom schemas |
--reserved | Only show reserved (built-in) schemas |
--category | Filter by category |
schema:get
Section titled “schema:get”Show full schema definition including properties, types, and field configurations.
tcms schema:get blogtcms schema:get blog --json| Argument | Required | Description |
|---|---|---|
id | Yes | Schema ID |
schema:export
Section titled “schema:export”Export a schema to a JSON file.
tcms schema:export blog --output=blog-schema.jsontcms schema:export blog| Argument | Required | Description |
|---|---|---|
id | Yes | Schema ID |
| Option | Description |
|---|---|
--output, -o | Output file path (omit for stdout) |
schema:import
Section titled “schema:import”Import a schema from a JSON file. Creates or updates the schema.
tcms schema:import my-schema.jsontcms schema:import my-schema.json --json| Argument | Required | Description |
|---|---|---|
file | Yes | Path to schema JSON file |
schema:lint
Section titled “schema:lint”Lint stored schemas without saving anything. schema:import validates on the way in, but schemas edited in place (by hand or by an AI agent) are never re-validated — the linter closes that gap. Errors are structural problems that break at runtime: no id property defined, required/index entries naming undefined properties, inheritFrom pointing at a missing schema, deck/card schemaref targets that don’t exist or contain deck-incompatible types, and meta-schema violations. Warnings flag agent-legibility gaps — properties without help text (help feeds the MCP tool catalog) and schemas without a description.
tcms schema:lint # all custom schemastcms schema:lint blog # one schema (reserved schemas allowed by ID)tcms schema:lint --strict # warnings also fail the runtcms schema:lint --json # machine-readable report| Argument | Required | Description |
|---|---|---|
id | No | Schema ID (default: lint all custom schemas) |
| Option | Description |
|---|---|
--strict | Treat warnings as failures |
Exit code is 0 when no errors were found (warnings allowed), 1 when errors were found — or warnings under --strict — so it slots into CI.
Collection Commands
Section titled “Collection Commands”collection:list
Section titled “collection:list”List all collections with their schema and object count.
tcms collection:listtcms collection:list --schema=blogtcms collection:list --category=Contenttcms collection:list --json| Option | Description |
|---|---|
--schema | Filter by schema type |
--category | Filter by category |
collection:get
Section titled “collection:get”Show collection metadata including schema, sort order, and object count.
tcms collection:get blogtcms collection:get blog --json| Argument | Required | Description |
|---|---|---|
id | Yes | Collection ID |
collection:query
Section titled “collection:query”Query a collection’s index with filtering, searching, sorting, and pagination.
tcms collection:query posts --search="photography" --limit=5tcms collection:query posts --include="featured:true" --sort="-date"tcms collection:query posts --exclude="draft:true" --limit=10 --offset=20tcms collection:query posts --filter="title:drone" --json| Argument | Required | Description |
|---|---|---|
id | Yes | Collection ID |
| Option | Description |
|---|---|
--search, -s | Full-text search query |
--filter | Contains filter (field:value) |
--include | Include filter (field:value,field:value) |
--exclude | Exclude filter (field:value,field:value) |
--sort | Sort by property (prefix with - for descending) |
--limit, -l | Maximum results (default: 20) |
--offset, -o | Number of results to skip (default: 0) |
JSON output:
{ "total": 37, "offset": 0, "limit": 5, "results": [...]}collection:export
Section titled “collection:export”Export a collection to JSON, CSV, or ZIP.
tcms collection:export blog --output=blog.jsontcms collection:export blog --format=csv --output=blog.csvtcms collection:export blog --format=zip --output=blog-backup.zip| Argument | Required | Description |
|---|---|---|
id | Yes | Collection ID |
| Option | Description |
|---|---|
--format, -f | Export format: json, csv, or zip (default: json) |
--output, -o | Output file path (omit for stdout; zip generates a default filename) |
The zip format includes all object JSON files and their associated media/assets. JSON export uses streaming for large collections when --output is specified.
collection:import
Section titled “collection:import”Import objects into a collection from a JSON or CSV file.
tcms collection:import blog posts.jsontcms collection:import blog data.csvtcms collection:import blog posts.json --updatetcms collection:import blog posts.json --format=json --json| Argument | Required | Description |
|---|---|---|
id | Yes | Collection ID |
file | Yes | Path to JSON or CSV file |
| Option | Description |
|---|---|
--format, -f | Import format: json or csv (auto-detected from extension) |
--update | Update existing objects instead of skipping |
Object Commands
Section titled “Object Commands”object:list
Section titled “object:list”List object IDs in a collection.
tcms object:list blogtcms object:list blog --limit=10 --offset=20tcms object:list blog --json| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
| Option | Description |
|---|---|
--limit, -l | Maximum results |
--offset, -o | Number of results to skip |
object:get
Section titled “object:get”Fetch a single object with all its properties.
tcms object:get blog my-posttcms object:get blog my-post --json| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
id | Yes | Object ID |
object:create
Section titled “object:create”Create one object from a JSON file (or stdin with -). The object goes through the full save pipeline — schema defaults fill in, validation runs, the index updates, and the object.created event fires — exactly as if it were saved from the admin. Refuses to overwrite an existing object, and refuses a JSON array (use collection:import for bulk data).
tcms object:create blog my-post.jsonecho '{"id":"hello","title":"Hello"}' | tcms object:create blog -tcms object:create blog my-post.json --json # echoes the saved object| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
file | Yes | Path to a JSON file holding one object, or - for stdin |
object:patch
Section titled “object:patch”Merge changes into an existing object, leaving every field you don’t mention untouched. Like object:create, it goes through the full save pipeline — validation runs, the collection index updates, and object.updated fires — so it stays consistent with the admin in a way that editing the JSON file by hand does not.
Use this for a targeted change to one object. object:create only makes new objects, and collection:import works in bulk.
tcms object:patch blog my-post patch.jsonecho '{"title":"New Title"}' | tcms object:patch blog my-post -tcms object:patch blog my-post patch.json --json # echoes the saved objectThe merge is shallow. Patching a structured field from the top level replaces it wholesale, which will quietly discard the parts you didn’t mention. Target the property instead to merge into it:
# Replaces the whole image field — name, size and dimensions are lostecho '{"image":{"alt":"A description"}}' | tcms object:patch image social -
# Changes only alt, preserving everything else on the fieldecho '{"alt":"A description"}' | tcms object:patch image social - --property=image| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
id | Yes | Object ID |
file | Yes | Path to a JSON file holding the fields to merge, or - for stdin |
| Option | Description |
|---|---|
--property | Merge into this property instead of the top level, preserving its other keys |
object:export
Section titled “object:export”Export a single object as JSON or ZIP (with assets).
tcms object:export blog my-post --output=my-post.jsontcms object:export blog my-post --format=zip --output=my-post.ziptcms object:export blog my-post| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
id | Yes | Object ID |
| Option | Description |
|---|---|
--format, -f | Export format: json or zip (default: json) |
--output, -o | Output file path (omit for stdout; zip generates a default filename) |
object:delete
Section titled “object:delete”Delete a single object. The deletion goes through the same index-aware path as the admin UI, so the collection’s .index.json and object count stay consistent — unlike removing the flat file by hand, which leaves them stale.
tcms object:delete blog my-posttcms object:delete blog my-post --force| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
id | Yes | Object ID |
| Option | Description |
|---|---|
--force, -f | Skip the confirmation prompt (required for non-interactive/CI use) |
Deck Commands
Section titled “Deck Commands”deck:import
Section titled “deck:import”Import items into a deck property from a JSON or CSV file.
tcms deck:import invoices inv-001 items line-items.jsontcms deck:import invoices inv-001 items line-items.csv --update| Argument | Required | Description |
|---|---|---|
collection | Yes | Collection ID |
object | Yes | Object ID |
property | Yes | Deck property name |
file | Yes | Path to JSON or CSV file |
| Option | Description |
|---|---|
--format, -f | Import format: json or csv (auto-detected from extension) |
--update | Update existing deck items instead of skipping |
Feed Commands
Section titled “Feed Commands”rss:import
Section titled “rss:import”Queue every entry from an RSS, Atom, or JSON feed into a target collection. The CLI counterpart to the Utilities → Import RSS admin page. Designed for cron — the admin form has a “Schedule with cron” panel that builds the exact command line for the configured import so operators can paste it directly into crontab.
# Basic import — auto field mapping, items queued as draftstcms rss:import https://example.com/feed.xml blog
# Publish immediatelytcms rss:import https://example.com/feed.xml blog --no-draft
# Drain the queue in the same cron runtcms rss:import https://example.com/feed.xml blog && tcms jobs:process| Argument | Required | Description |
|---|---|---|
url | Yes | RSS / Atom / JSON Feed URL |
collection | Yes | Target collection ID |
| Option | Description |
|---|---|
--draft / --no-draft | Queue items as drafts (default) or publish immediately |
--map, -m | Field mapping in the form feedField=collectionField. Repeat the option or comma-separate within one value. See below. |
--user-agent | User-Agent for the feed request. See below. |
--json | Output JSON (success status + count) |
When a feed returns 403
Section titled “When a feed returns 403”Feed requests identify themselves as TotalCMS/{version} (+https://totalcms.co). Some hosts — Cloudflare-fronted sites in particular — block requests from unrecognized or generic clients outright, and a feed that opens fine in a browser can still return 403 to a server.
If a host rejects the default, --user-agent sets your own:
tcms rss:import https://example.com/feed.xml blog \ --user-agent "AcmeNews/1.0 (+https://acme.example)"Identify yourself honestly — a contactable URL is what gets a well-run site to allowlist you. Impersonating a browser tends to be treated as evasion and blocked harder.
Field mapping
Section titled “Field mapping”The importer maps these eight feed-side fields to your collection’s properties:
| Feed field | Default collection property |
|---|---|
title | title |
content | content |
summary | summary |
date | date |
author | author |
categories | categories |
link | media |
image | image |
To override a default mapping, pass --map feedField=collectionProperty. To drop a field entirely (don’t write it to the object), map it to an empty string with --map feedField=.
# Map the feed's `title` into your schema's `heading` property,# and the feed's `content` into your schema's `body` property.tcms rss:import https://example.com/feed.xml blog \ --map title=heading \ --map content=body
# Same thing, comma-separated single argument (handy in crontab one-liners).tcms rss:import https://example.com/feed.xml blog --map "title=heading,content=body"
# Drop the link/categories fields entirely; remap the rest.tcms rss:import https://example.com/feed.xml news \ --map title=headline \ --map content=body \ --map image=hero \ --map link= \ --map categories=
# Realistic news-import recipe targeting a schema with# `heading`, `body`, `excerpt`, `published`, `byline`, `tags`, `hero`.tcms rss:import https://example.com/feed.xml news --no-draft \ --map title=heading \ --map content=body \ --map summary=excerpt \ --map date=published \ --map author=byline \ --map categories=tags \ --map image=heroCron example
Section titled “Cron example”# Hourly RSS pull — drain the queue right after queuing0 * * * * /usr/local/bin/php /var/www/site/resources/bin/tcms rss:import "https://example.com/feed.xml" blog --no-draft --map "title=heading,content=body" && /usr/local/bin/php /var/www/site/resources/bin/tcms jobs:processThe admin UI’s “Schedule with cron” panel under Utilities → Import RSS prints this command for you with your site’s PHP and install paths already filled in — open the panel, copy, paste into crontab.
JumpStart Commands
Section titled “JumpStart Commands”jumpstart:export
Section titled “jumpstart:export”Export all site data (schemas, collections, objects, templates) as a JumpStart file.
tcms jumpstart:export --output=my-site.jsontcms jumpstart:export --name="My Site" --description="Full site export"tcms jumpstart:export --json| Option | Description |
|---|---|
--name | Name for the export |
--description | Description for the export |
--output, -o | Output file path (generates default filename if omitted) |
jumpstart:import
Section titled “jumpstart:import”Import a JumpStart file to set up schemas, collections, objects, and templates.
tcms jumpstart:import my-site.jsontcms jumpstart:import my-site.json --json| Argument | Required | Description |
|---|---|---|
file | Yes | Path to JumpStart JSON file |
Sync Commands
Section titled “Sync Commands”Push and pull schemas, templates, site-machinery objects, and collection settings between a local development instance and a production server. Configure the production server URL and API key in Settings > Sync in the admin dashboard. Full details, including the object-seeding workflow: Sync guide.
3.5.1 note:
--collectionsnow means collection settings (it used to mean objects from five allowlisted collections). The flag formerly namedcollection-metais gone —--collectionsdoes its job now. Objects for those five collections move via their own feature flags instead (--pages,--dataviews,--mailer,--mcp-prompts,--automations), and any other collection’s object data can be seeded with--objects. Full breaking-changes writeup: Sync guide.
Push schemas, templates, site-machinery objects, collection settings, and (optionally) seed object data to the production server.
tcms pushtcms push --dry-runtcms push --schemas=blog,productstcms push --templates=blog-post,sidebartcms push --pagestcms push --pages=home,abouttcms push --collections=comparisons,builder-pagestcms push --objects=blogtcms push --objects=blog:launch-day --overwrite --dry-runtcms push --schemas=blog --templates=blog-post --dry-run| Option | Description |
|---|---|
--schemas | Comma-separated schema IDs to push |
--templates | Comma-separated template IDs to push |
--pages[=id,id] | Site Builder pages — all of them, or just the ones listed |
--dataviews[=id,id] | Data Views — all of them, or just the ones listed |
--mailer[=id,id] | Mailer templates — all of them, or just the ones listed |
--mcp-prompts[=id,id] | MCP prompts — all of them, or just the ones listed |
--automations[=id,id] | Automations — all of them, or just the ones listed |
--collections | Comma-separated collection IDs whose SETTINGS to push (any collection; counters never travel) |
--objects | Seed object data: collection or collection:id,id, repeatable. Existing objects on the target are skipped unless --overwrite is given. Push-only |
--overwrite | Let --objects overwrite objects that already exist on the target |
--force | Allow --overwrite without a prior --dry-run when not attached to a terminal |
--dry-run | Compare both sides: per-item unchanged/differs/new status with newer-side hints |
Pull schemas, templates, site-machinery objects, and collection settings from the production server. --objects/--overwrite have no pull equivalent — seeding is push-only.
tcms pulltcms pull --dry-runtcms pull --schemas=blogtcms pull --templates=blog-post,sidebartcms pull --pages=home,abouttcms pull --collections=comparisons| Option | Description |
|---|---|
--schemas | Comma-separated schema IDs to pull |
--templates | Comma-separated template IDs to pull |
--pages[=id,id] | Site Builder pages — all of them, or just the ones listed |
--dataviews[=id,id] | Data Views — all of them, or just the ones listed |
--mailer[=id,id] | Mailer templates — all of them, or just the ones listed |
--mcp-prompts[=id,id] | MCP prompts — all of them, or just the ones listed |
--automations[=id,id] | Automations — all of them, or just the ones listed |
--collections | Comma-separated collection IDs whose SETTINGS to pull (any collection; counters never travel) |
--dry-run | Compare both sides: per-item unchanged/differs/new status with newer-side hints |
What gets synced: Custom schemas, custom templates, collection settings for any collection (never its counters), objects for the five feature-flagged collections — builder-pages (--pages), mailer, mcp-prompt (--mcp-prompts), dataviews, automations — and, push-only, seeded object data for any other seedable collection via --objects.
What never gets synced: Objects in custom collections you haven’t named with --objects; the image, gallery, file, depot, and playground collections, which can never be seeded; image/file/gallery/depot fields on any object, which are always stripped from every payload; media/images; system settings; API keys; reserved schemas. A custom collection’s schema syncs; its objects only travel if you explicitly seed them.
Filter semantics: a bare tcms push or tcms pull is a full mirror of schemas, templates, the five feature-flagged collections, and collection settings — it never seeds --objects, which only runs when named explicitly. The moment any filter flag is given, the categories you did not mention are excluded entirely, so tcms push --schemas=blog moves the blog schema and nothing else.
Backups: before an overwrite lands, the receiving instance snapshots the current version to tcms-data/.system/backups/{schemas,objects}/... (ten most recent per item). See the Sync guide for details.
Cache & Jobs
Section titled “Cache & Jobs”cache:clear
Section titled “cache:clear”Clear all caches.
tcms cache:cleartcms cache:clear --jsonHow this reaches the web server. APCu is per-process, so a CLI run cannot clear the cache the web server is holding — the two never share memory. Instead, cache:clear clears what it can reach directly (filesystem, Redis, Memcached) and writes a signal file to tcms-data/.system/.cache_invalidate. The next request to hit the site replays that signal and clears APCu in the web process.
That means the clear does not take effect until someone loads a page. If you clear from a deploy script and immediately check the site, the very request you make is the one that applies it — so a single reload can still look stale. Load the page twice.
If a clear appears not to have worked, check whether the signal file is still sitting there:
ls tcms-data/.system/.cache_invalidatePresent means no request has replayed it yet. Gone means it was applied.
Alternatives when you are iterating. Two options avoid the round trip entirely:
- Turn on Developer Mode in the admin. Cache reads are bypassed outright while it is active, so you see fresh output on every request without clearing anything. This is the right choice while you are actively editing templates or content.
- Hit the HTTP endpoint at
/api/emergency/cache/clear, which clears in the web process directly rather than by signal. It needs no login, and it is rate-limited to one call per IP every 15 minutes.
Note the
/apiprefix —/emergency/cache/clearwithout it returns a 404.
jobs:process
Section titled “jobs:process”Process the pending job queue. This is typically run via cron.
tcms jobs:processtcms jobs:process -vtcms jobs:process --memory=1Gtcms jobs:process --json| Option | Description |
|---|---|
--memory, -m | Memory limit (default: 512M) |
-v | Verbose output with per-job details |
Cron setup:
* * * * * php /path/to/resources/bin/tcms jobs:processautomations:process
Section titled “automations:process”Fire due scheduled automations. Runs on its own cron line, parallel to jobs:process, so a large import backlog in the job queue never delays a time-sensitive scheduled automation. Single-flight locked, so overlapping cron ticks can’t double-fire the same run.
tcms automations:processtcms automations:process --jsonWebhook and event triggers do not depend on this command — webhooks fire on HTTP request and events fire when the originating write happens; their async runs are drained on the next tick.
Cron setup (add this as a second line, alongside jobs:process):
* * * * * php /path/to/resources/bin/tcms automations:processMCP Commands
Section titled “MCP Commands”See the MCP Server guide for the full server documentation.
mcp:status
Section titled “mcp:status”Show the MCP server’s operator-facing health: enabled state, public-access switch, edition gate, tool prefix, and the tool list each persona sees. Saved-query tools defined in collection MCP cards are included and annotated (saved query).
tcms mcp:statustcms mcp:status --json # persona tool lists plus a schema_tools arraymcp:test
Section titled “mcp:test”Invoke a tool locally without going through the HTTP endpoint — useful for verifying a tool’s output and persona visibility before an agent connects.
tcms mcp:test query_collection --params='{"collection":"blog","limit":3}'tcms mcp:test query_collection --params='{"collection":"blog"}' --persona=publicMaintenance Commands
Section titled “Maintenance Commands”repair:index
Section titled “repair:index”Rebuild a collection’s .index.json and totalObjects count from the objects on disk. Use this when the index has drifted from the actual files — for example after an object’s flat file was added or removed out-of-band. (search:reindex only touches the search provider; repair:files only rebuilds file/image metadata.)
tcms repair:index blogtcms repair:index --all| Argument | Required | Description |
|---|---|---|
collection | No | Collection ID (omit and use --all to rebuild every collection) |
| Option | Description |
|---|---|
--all | Rebuild the index for every collection |
Update Commands
Section titled “Update Commands”update:check
Section titled “update:check”Check for available updates from the license server.
tcms update:checktcms update:check --jsonJSON output:
{ "current": "3.2.2", "available": true, "version": "3.5.0", "releaseDate": "2026-04-10", "severity": "minor", "changelog": "New features and improvements", "downloadUrl": "/version/download/3.5.0"}update:apply
Section titled “update:apply”Download and apply an available update. The site enters maintenance mode during the swap.
tcms update:applytcms update:apply --forcetcms update:apply --json| Option | Description |
|---|---|
--force | Skip confirmation prompt |
The previous version is backed up automatically for rollback.
update:rollback
Section titled “update:rollback”Roll back to the previous version after a failed or unwanted update.
tcms update:rollbacktcms update:rollback --force| Option | Description |
|---|---|
--force | Skip confirmation prompt |
Restores the backup directory created during the most recent update.
Extension Commands
Section titled “Extension Commands”extension:list
Section titled “extension:list”List all discovered extensions with their status.
tcms extension:listtcms extension:list --jsonJSON output:
[ { "id": "acme/seo-pro", "name": "SEO Pro", "version": "1.2.0", "enabled": true, "error": null }]extension:enable
Section titled “extension:enable”Enable a discovered extension.
tcms extension:enable acme/seo-protcms extension:enable acme/seo-pro --json| Argument | Required | Description |
|---|---|---|
id | Yes | Extension ID (e.g. vendor/extension-name) |
extension:disable
Section titled “extension:disable”Disable an extension without removing it.
tcms extension:disable acme/seo-protcms extension:disable acme/seo-pro --json| Argument | Required | Description |
|---|---|---|
id | Yes | Extension ID (e.g. vendor/extension-name) |
extension:remove
Section titled “extension:remove”Remove an extension’s files. Extension data in tcms-data is preserved.
tcms extension:remove acme/seo-protcms extension:remove acme/seo-pro --force| Argument | Required | Description |
|---|---|---|
id | Yes | Extension ID (e.g. vendor/extension-name) |
| Option | Description |
|---|---|
--force, -f | Skip confirmation prompt |