| name | mops-cli |
| description | Manage Motoko projects with the mops CLI — toolchain pinning, dependency management, type-checking, building, and linting. Use when working with mops.toml, mops.lock, running mops commands, adding/removing packages, pinning moc or lintoko versions, checking or building canisters, configuring moc flags, or setting up a new Motoko project. |
Mops CLI
Opinionated guide for Motoko projects. Covers project config, dependency management, type-checking, building, and linting.
Key Principles
- No dfx — always pin
moc in [toolchain]. Use the newest moc version.
- No
mo:base — it is deprecated. Always use mo:core (import Array "mo:core/Array").
- All config in
mops.toml — canisters, moc flags, toolchain versions, build settings.
- Canister-centric workflow — define all canisters in
[canisters]; never pass file paths to mops check. Exception: library packages (no [canisters]) use file paths directly: mops check src/**/*.mo.
Project Setup
Minimal mops.toml
[toolchain]
moc = "1.7.0"
lintoko = "0.10.0"
[dependencies]
core = "2.5.0"
[moc]
args = ["--default-persistent-actors", "-W=M0223,M0236,M0237"]
[canisters.backend]
main = "src/backend/main.mo"
[canisters.backend.migrations]
chain = "src/backend/migrations"
check-limit = 10
[canisters.backend.check-stable]
path = ".old/src/backend/dist/backend.most"
[build]
outputDir = "src/backend/dist"
args = ["--release"]
check-stable verifies stable variable compatibility against a .most file from the deployed version. For a new project with no prior deployment, create a trivial .most file representing an empty actor:
// Version: 1.0.0
actor {
};
Optional canister fields: candid (path to .did for compatibility checking), initArg (Candid-encoded init args).
Warning Flags
-W=M0223,M0236,M0237 — redundant type instantiation (M0223), suggest contextual dot notation (M0236), suggest redundant explicit arguments (M0237). These are allowed (disabled) by default; -W= enables them as warnings.
Moc Args Layering
Flags are applied in this order (later overrides earlier):
[moc].args — global, all commands (check, build, test, etc.)
[build].args — build only (e.g. --release)
[canisters.<name>.migrations] — auto-injected --enhanced-migration (managed by mops)
[canisters.<name>].args — per-canister
- CLI
-- <flags> — one-off overrides
Core Commands
mops install
mops install
Run after cloning or after manual mops.toml edits. Updates mops.lock. In CI, uses --lock check by default (fails if lockfile is stale).
mops add <package>
mops add core
mops add core@2.5.0
mops add --dev test
Updates mops.toml and mops.lock.
mops check
Primary correctness command — runs moc check, then check-stable (if configured), then lint (if lintoko is in toolchain).
mops check
mops check backend
mops check --fix
mops check --verbose
mops check -- -Werror
Always use canister names, not file paths. Per-canister args from mops.toml are applied automatically.
--fix applies machine-applicable fixes from both moc and lintoko in one pass.
mops build
mops build
mops build backend
mops build --verbose
mops build -- --ai-errors
Produces .wasm, .did, and .most files in [build].outputDir (default .mops/.build).
mops toolchain
mops toolchain use moc 1.7.0
mops toolchain use moc latest
mops toolchain use lintoko 0.10.0
mops toolchain update moc
mops toolchain update
mops toolchain bin moc
Agent note: toolchain use <tool> without a version opens an interactive picker — do not use in scripts or agents. Always pass a version or latest. toolchain update only works when the tool already has a [toolchain] entry.
Enhanced migrations
When [canisters.<name>.migrations] is configured, mops check, mops build, and mops check-stable automatically inject --enhanced-migration. Do not add --enhanced-migration to [canisters.<name>].args — mops will error.
Create migration files directly in the chain directory.
check-limit (optional) caps how many recent chain files mops check and mops lint consider — useful when the chain grows long and re-checking every old migration slows feedback down. mops build is unaffected by check-limit. When the limit kicks in, mops stages the included files into .migrations-<canister>/ next to the chain directory (auto-.gitignored). moc diagnostics may then print paths there — the real file lives in the chain directory with the same name.
mops remove <package>
mops remove base
Dependency Management
mops outdated
mops update
mops update core
mops update --major
mops update --patch
mops sync
Other Commands
mops test
Tests live in test/*.test.mo:
mops test
mops test my-test
mops test --mode wasi
mops test --reporter verbose
mops test --watch
mops lint
Runs lintoko (also runs automatically as part of mops check when lintoko is in toolchain):
mops lint
mops lint --fix
mops lint <name>
When [canisters.<name>.migrations].check-limit is set, mops lint skips the trimmed chain migrations to match what moc sees during mops check. To lint a trimmed migration on demand, pass an explicit filter (e.g. mops lint OldMigrationName).
mops format
mops format
mops format --check
Common Patterns
Warning suppression for a canister
Use per-canister args (not global) for suppressions:
[canisters.backend]
main = "src/backend/main.mo"
args = ["-A=M0198"]
New project
mops init -y
mops toolchain use moc latest
mops toolchain use lintoko latest
mops add core
Then configure [moc].args, [canisters], and [build] in mops.toml.
To update tools later: mops toolchain update moc or mops toolchain update (all tools).