dotnet-tools and Local Tool Manifests
.NET CLI tools are console applications distributed as NuGet packages. You can install them globally, but for team projects, local tool manifests are the better approach — they version your tools alongside your code and ensure everyone uses the same versions.
Global vs Local Tools
Global tools are installed per-user and available everywhere:
dotnet tool install -g dotnet-ef
The problem is that different projects may need different versions of the same tool. Global tools also aren't tracked in source control, so a new team member has to know which tools to install and at which versions.
Local tools solve this. They're declared in a manifest file that lives in your repository.
Creating a Tool Manifest
Initialise a manifest in your repository root:
dotnet new tool-manifest
This creates .config/dotnet-tools.json:
{
"version": 1,
"isRoot": true,
"tools": {}
}
Now install tools locally:
dotnet tool install dotnet-ef
dotnet tool install dotnet-format
dotnet tool install dotnet-reportgenerator-globaltool
Each command updates the manifest:
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.1",
"commands": ["dotnet-ef"],
"rollForward": false
},
"dotnet-format": {
"version": "5.1.250801",
"commands": ["dotnet-format"],
"rollForward": false
},
"dotnet-reportgenerator-globaltool": {
"version": "5.4.3",
"commands": ["reportgenerator"],
"rollForward": false
}
}
}
Restoring Tools
When a team member clones the repository, they run:
dotnet tool restore
This installs every tool listed in the manifest at the exact specified version. No guesswork, no "works on my machine" problems.
Running Local Tools
Local tools are invoked with dotnet tool run or simply dotnet followed by the tool command:
# These are equivalent
dotnet tool run dotnet-ef migrations add Initial
dotnet ef migrations add Initial
The CLI automatically finds the manifest and uses the local version.
Using Tools in CI
Tool manifests work naturally in CI pipelines. Add a restore step:
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Restore tools
run: dotnet tool restore
- name: Run EF migrations check
run: dotnet ef migrations has-pending-changes --project src/MyApp
- name: Generate coverage report
run: |
dotnet test --collect:"XPlat Code Coverage"
dotnet reportgenerator -reports:**/coverage.cobertura.xml -targetdir:coverage
Because the versions are pinned in the manifest, CI always uses exactly the same tool versions as local development.
Updating Tools
Update a specific tool:
dotnet tool update dotnet-ef
Or update all tools at once:
dotnet tool update --all
The manifest file is updated automatically. Commit the change, and the whole team gets the update on their next dotnet tool restore.
Common Tools Worth Adding
Here are tools that work well as local tools in .NET projects:
| Tool | Package | Purpose |
|---|---|---|
| EF Core CLI | dotnet-ef |
Database migrations |
| ReportGenerator | dotnet-reportgenerator-globaltool |
Coverage reports |
| Stryker | dotnet-stryker |
Mutation testing |
| CSharpier | csharpier |
Code formatting |
| dotnet-outdated | dotnet-outdated-tool |
Check for outdated packages |
Automating Tool Restore
You can ensure tools are always restored by hooking into MSBuild. Add a target to your Directory.Build.targets:
<Project>
<Target Name="RestoreTools" BeforeTargets="Restore">
<Exec Command="dotnet tool restore" />
</Target>
</Project>
Now dotnet restore also restores your tools. This is especially useful for developers who might forget the separate dotnet tool restore step.
Summary
Local tool manifests are a small file with a large impact. They eliminate version drift between developers, make CI pipelines reproducible, and document exactly which tools a project depends on. If you're still installing .NET tools globally for project work, switch to a manifest — it takes thirty seconds to set up and saves hours of debugging mismatched tool versions.