Scaffolding for .NET that understands the project you're already in.
dotnet new creates projects from scratch. MTC does that too, but its real job starts
afterwards: run mtc add feature Product anywhere inside your solution and MTC detects
whether you're in Clean Architecture, MVC or Vertical Slice, then writes each
generated file into the layer where it belongs.
cd MyShop # a Clean Architecture solution
mtc add feature Product --fields "Name:string Price:decimal"
# → MyShop.Domain/Entities/Product.cs
# → MyShop.Application/Features/Products/Commands/CreateProduct/...
# → MyShop.API/Controllers/ProductsController.csNo config file, no flags telling it what your architecture is. It looks at your solution and figures it out.
Pick whichever fits your setup.
| Method | Command |
|---|---|
| .NET tool (any OS) | dotnet tool install -g MTC |
| Arch Linux (AUR) | yay -S mtc-bin |
| Debian / Ubuntu | download the .deb from Releases, then sudo dpkg -i mtc_<version>_amd64.deb |
| Manual binary | download the .tar.gz for your OS from Releases |
Manual install details
- Linux / macOS — extract the archive, move the
mtcbinary somewhere on yourPATH(/usr/local/binworks), and keep thetemplates/folder next to it. - Windows — extract the archive (Windows 10/11 handles
.tar.gznatively) and add the extracted folder to yourPATH.
Requires the .NET 10 SDK if you install as a .NET tool. The standalone binaries are self-contained and need nothing.
Create a project, then grow it.
# 1. Make a folder and scaffold the solution into it
mkdir MyShop && cd MyShop
mtc new CleanArch MyShop
# 2. Add a feature — MTC places the files for you
mtc add feature Product --fields "Name:string Price:decimal Stock:int"That's the whole loop. Step 2 is the one you'll run over and over.
mtc newwrites into the current directory (or--output), it does not create a containing folder for you. Make the folder first, as above.
Creates a new project from a template.
mtc new ConsoleApp MyApp
mtc new MvcMonolith MyWebApp
mtc new CleanArch MyShop
mtc new VerticalSlice MyApi| Option | Description |
|---|---|
[name] |
Project name. Defaults to the output directory name. |
-o, --output |
Where to create it. Defaults to the current directory. |
-n, --name |
Same as the positional [name]. Takes precedence if both are given. |
--force |
Overwrite files that already exist. |
Generates a full feature — entity, commands/queries, and controller — placed according to the detected architecture.
mtc add feature Product --fields "Name:string Price:decimal Stock:int"| Architecture | Where the files land |
|---|---|
| Clean Architecture | split across *.Domain, *.Application and *.API |
| Vertical Slice | a new folder under Features/ |
| MVC Monolith | Controllers/, Models/ and Views/ |
mtc add value-object Money --fields "Amount:decimal Currency:string"Goes to *.Domain/ValueObjects in Clean Architecture, Models/ValueObjects in MVC, and
./ValueObjects otherwise.
mtc add dto UserDto --fields "Username:string Email:string Age:int"Goes to *.Application/DTOs in Clean Architecture, Models/DTOs in MVC, and ./DTOs
otherwise.
Lists every available template with its description, author and version.
Persists preferences in ~/.mtc/config.json.
mtc config set Author "Your Name"
mtc config get Author
mtc config listShows what MTC detected — solution path and architecture. Run this first whenever a generate command says it can't figure out your project.
mtc debug-contextEvery generate command checks its targets first. If a file it would write already exists, MTC writes nothing at all and tells you what got in the way:
$ mtc add feature Product --fields "Name:string Price:decimal"
Error: 4 file(s) already exist and would be overwritten:
MyShop.Domain/Entities/Product.cs
MyShop.Application/Features/Products/Queries/GetProducts/GetProductsQuery.cs
MyShop.Application/Features/Products/Commands/CreateProduct/CreateProductCommand.cs
MyShop.API/Controllers/ProductsController.cs
Nothing was written. Re-run with --force to replace them.
It exits with code 1, so scripts and CI notice. Pass --force when you do want the
files regenerated. Generation is all-or-nothing — a conflict never leaves you with a
half-written feature.
--fields takes a space-separated list of Name:type pairs:
--fields "Name:string Price:decimal IsActive:bool CreatedAt:datetime Id:guid"| Alias | Generates |
|---|---|
string |
string |
int, long, short, byte |
int, long, short, byte |
float, double, decimal |
float, double, decimal |
bool |
bool |
char |
char |
datetime |
DateTime |
dateonly |
DateOnly |
timeonly |
TimeOnly |
guid |
Guid |
Anything else is passed through unchanged, so Status:OrderStatus works if that type
exists in your project.
Reference-typed properties are generated with the required modifier, so the code MTC
writes compiles clean under <Nullable>enable</Nullable>.
MTC walks up from your current directory until it finds a .sln, then inspects the
projects around it:
- projects ending in
.Domain,.Application,.Infrastructureand.API→ Clean Architecture - a
Features/folder → Vertical Slice - a project holding
Controllers/,Views/andModels/→ MVC Monolith
If none match, MTC stops and tells you rather than guessing. mtc debug-context shows
exactly what it saw.
dotnet build
dotnet testIntegration tests drive the compiled binary, so build before testing.
Maintainer scripts: ./publish.sh (cross-platform binaries), ./publish_aur.sh,
./publish_deb.sh.
MIT — see LICENSE.