This project is an MCP (Model Context Protocol) server that integrates with the .NET SDK. It provides both CLI command execution and direct SDK integration through official NuGet packages, offering rich metadata access and type-safe operations.
Important: This package is designed exclusively as an MCP server for AI assistants. It is not intended for use as a library or for programmatic consumption in other .NET applications. The only supported use case is running it as an MCP server via dnx or dotnet run.
- .NET 10.0: The latest version of .NET (LTS - Long Term Support)
- Model Context Protocol SDK: Version 1.0.0
- Stdio Transport: Communication via standard input/output
- Microsoft.Extensions.Hosting: For application lifecycle management
- Microsoft.TemplateEngine.*: Direct integration with .NET Template Engine (v10.0.101)
- Microsoft.Build.*: MSBuild integration for framework validation (v18.0.2)
The server uses a hybrid architecture:
- SDK Integration - For metadata, discovery, and validation (Template Engine, MSBuild)
- CLI Execution - For actual command execution (proven, reliable)
- Use PascalCase for class names, method names, and public members
- Use camelCase for local variables and parameters
- Use descriptive names that clearly indicate purpose
- Consolidated tool method names follow the pattern:
Dotnet{Noun}(e.g.,DotnetProject,DotnetPackage,DotnetSolution)- Each consolidated tool accepts an
actionparameter to specify the operation - This aligns with the consolidated tool design for better AI orchestration
- Each consolidated tool accepts an
- Helper class methods should be descriptive:
GetInstalledTemplatesAsync,IsLtsFramework
- Mark tool classes with
[McpServerToolType] - Mark tool methods with
[McpServerTool] - Prefer XML doc comments (
/// <summary>,/// <param>) for tool and parameter descriptions - Tool methods should be
partialso the MCP SDK analyzers can generate toolDescriptionmetadata from XML docs - Avoid manual
[Description("...")]on tools/parameters unless explicitly required for compatibility - Keep descriptions clear and concise
Important (generator compatibility):
- Consolidated tools commonly take action enums from
DotNetMcp.Actions. - The MCP XML-doc generator emits partial method signatures that reference parameter types by simple name.
- Ensure action enums resolve by keeping
DotNetMcp.Actionsin scope (this repo usesDotNetMcp/GlobalUsings.cswithglobal using DotNetMcp.Actions;). - Use
[McpMeta]attributes to provide additional metadata for AI assistants:- Category tags:
[McpMeta("category", "template")]- Groups related tools (template, project, package, solution, sdk, etc.) - Priority hints:
[McpMeta("priority", 10.0)]- Suggests relative importance (1.0-10.0 scale) - Boolean flags:
[McpMeta("commonlyUsed", true)]- Indicates frequently used tools - Capability markers:
[McpMeta("isLongRunning", true)]- Warns about long-running operations - Version requirements:
[McpMeta("minimumSdkVersion", "6.0")]- Documents SDK version dependencies - JSON values:
[McpMeta("tags", JsonValue = """["a","b"]""")]- Complex metadata as JSON
- Category tags:
- Each tool method should handle a single .NET CLI command
- Use the
ExecuteDotNetCommandhelper method for all dotnet executions - Always use
async/awaitfor I/O operations - Return meaningful string results that include both stdout and stderr
- Helper classes for SDK integration:
TemplateEngineHelper- Template operationsFrameworkHelper- Framework validationDotNetSdkConstants- Type-safe constants
- Prefer
Path.Join(...)overPath.Combine(...)whenever possible. - Avoid
Path.Combinebecause it has "rooted path reset" behavior when a later segment is rooted (or starts with a directory separator), which can lead to surprising bugs and repeated analyzer/security findings.
- This repo uses the standard .NET local tool manifest location:
.config/dotnet-tools.json. - Avoid creating or editing a root-level
dotnet-tools.json(it is not the default manifest path fordotnet tool restore).
.config/dotnet-tools.jsonis source-controlled to keep tool versions reproducible in CI and agent sessions.- Do not modify tool versions in this file unless the user explicitly asks you to update tools.
- If a command/tool run causes the manifest to change (for example, bumping a tool version), revert it unless the change was explicitly requested.
- This repo includes a top-level
_commentfield in the manifest as a reminder; do not remove it.
When building the project, always use the full path to the project file:
dotnet build /path/to/dotnet-mcp/DotNetMcp/DotNetMcp.csprojImportant: If a build fails due to inability to find the project file, pass the full path to the .csproj file. Do not rely on relative paths or current directory assumptions.
After making code changes:
- Build with full path:
dotnet build --project [full path to .csproj] - Check for compilation errors
- Test the MCP server manually if significant changes were made
When running tests:
- Use
dotnet test --project ...to run a specific test project.- Do not pass a
.csprojas a positional argument;dotnet testwill warn:Specifying a project for 'dotnet test' should be via '--project'.
- Do not pass a
- Use
dotnet test --solution ...to run from the solution. - Use Microsoft Testing Platform filters after
--(for example--filter-class ...).
// Get templates programmatically
var engineEnvironmentSettings = new EngineEnvironmentSettings(
new DefaultTemplateEngineHost("dotnet-mcp", "1.0.0"),
virtualizeSettings: true);
var templatePackageManager = new TemplatePackageManager(engineEnvironmentSettings);
var templates = await templatePackageManager.GetTemplatesAsync(default);// Validate and get framework info
if (FrameworkHelper.IsValidFramework(framework))
{
var description = FrameworkHelper.GetFrameworkDescription(framework);
var isLts = FrameworkHelper.IsLtsFramework(framework);
}// Use strongly-typed constants
var framework = DotNetSdkConstants.TargetFrameworks.Net100;
var config = DotNetSdkConstants.Configurations.Release;
var runtime = DotNetSdkConstants.RuntimeIdentifiers.LinuxX64;When adding a new .NET CLI tool:
- Add a new method to the
DotNetCliToolsclass - Follow the naming convention:
Dotnet{Noun}{Verb}(e.g.,DotnetProjectBuild,DotnetPackageAdd) - Add XML doc comments (
/// <summary>,/// <param>) for the tool and all parameters - Make the tool method
partialso the MCP XML-doc generator can emit toolDescriptionmetadata - Use nullable types for optional parameters with default values
- Build the command string carefully, escaping paths with quotes
- Call
ExecuteDotNetCommand(args)to execute - Test the tool manually before committing
Following the dotnet_{noun}_{verb} pattern:
- Template tools:
DotnetTemplateList,DotnetTemplateSearch,DotnetTemplateInfo - Project tools:
DotnetProjectNew,DotnetProjectBuild,DotnetProjectRun - Package tools:
DotnetPackageAdd,DotnetPackageList - SDK tools:
DotnetSdkInfo,DotnetSdkVersion,DotnetSdkList
Use SDK Integration when:
- You need metadata or discovery (templates, frameworks)
- You want to validate input before execution
- You need structured data
- You want type-safe operations
Use CLI Execution when:
- Actually performing operations (build, run, test)
- The SDK doesn't expose the functionality
- CLI is the proven, tested method
using System.Text;
using ModelContextProtocol.Server;
namespace DotNetMcp;
public sealed partial class DotNetCliTools
{
/// <summary>
/// Create a new .NET project from a template, validating inputs before executing.
/// Uses SDK integration for validation and CLI execution for the actual operation.
/// </summary>
/// <param name="template">Template short name (e.g., 'console', 'webapi', 'classlib')</param>
/// <param name="name">Project name</param>
/// <param name="output">Output directory</param>
/// <param name="machineReadable">Return structured JSON output for both success and error responses instead of plain text</param>
[McpServerTool]
[McpMeta("category", "project")]
[McpMeta("priority", 10.0)]
[McpMeta("commonlyUsed", true)]
public async partial Task<string> DotnetProjectNewValidated(
string? template = null,
string? name = null,
string? output = null,
bool machineReadable = false)
{
// 1. SDK Integration: Validate template exists (fast + type-safe)
var templateValidation = await ParameterValidator.ValidateTemplateAsync(template);
if (!templateValidation.IsValid)
{
if (machineReadable)
{
var error = ErrorResultFactory.CreateValidationError(
templateValidation.ErrorMessage!,
parameterName: "template",
reason: string.IsNullOrWhiteSpace(template) ? "required" : "not found");
return ErrorResultFactory.ToJson(error);
}
return $"Error: {templateValidation.ErrorMessage}";
}
// 2. CLI Execution: Actually create the project
var args = new StringBuilder($"new {template}");
if (!string.IsNullOrEmpty(name)) args.Append($" -n \"{name}\"");
if (!string.IsNullOrEmpty(output)) args.Append($" -o \"{output}\"");
return await ExecuteDotNetCommand(args.ToString(), machineReadable);
}
// ...other tool methods and helpers...
}Note: Prefer XML docs over [Description] attributes for new tools. If you add a new tool method, make it partial and document it with /// <summary> and /// <param> so descriptions stay consistent in tool listing and tests.
Provides strongly-typed constants for common SDK values.
- TargetFrameworks: TFMs (net10.0, net8.0, etc.)
- Configurations: Debug, Release
- RuntimeIdentifiers: win-x64, linux-x64, etc.
- Templates: Common template short names
- CommonPackages: Well-known NuGet packages
Integration with .NET Template Engine.
GetInstalledTemplatesAsync()- List all templatesGetTemplateDetailsAsync(shortName)- Get template detailsSearchTemplatesAsync(searchTerm)- Search templatesValidateTemplateExistsAsync(shortName)- Validate template
Framework validation and information.
IsValidFramework(framework)- Validate TFMGetFrameworkDescription(framework)- Get friendly nameIsLtsFramework(framework)- Check if LTSIsModernNet/IsNetCore/IsNetFramework/IsNetStandard()- ClassificationGetLatestRecommendedFramework()- Latest versionGetLatestLtsFramework()- Latest LTS
- Automated tests: Run the test suite with
dotnet test --solution DotNetMcp.slnx- 703 passing tests covering all 74 MCP tools
- MCP conformance tests validate protocol compliance
- See doc/testing.md for details
- Build the project:
dotnet build --project [full path to .csproj] - Run the server:
dotnet run --project [full path to .csproj] - Manual testing: Test with MCP Inspector or Claude Desktop
- Verify all commands work with various parameter combinations
- Test SDK integration: Try the new template and framework info tools
var args = new StringBuilder("command");
// Add required parameters
args.Append($" {value}");
// Add optional flags
if (condition)
args.Append(" --flag");
// Add optional parameters with values
if (!string.IsNullOrEmpty(value))
args.Append($" --option \"{value}\"");Always use the ExecuteDotNetCommand helper method which:
- Captures stdout and stderr
- Reports exit codes
- Handles process lifecycle correctly
try
{
var engineEnvironmentSettings = new EngineEnvironmentSettings(
new DefaultTemplateEngineHost("dotnet-mcp", "1.0.0"),
virtualizeSettings: true);
var templatePackageManager = new TemplatePackageManager(engineEnvironmentSettings);
var templates = await templatePackageManager.GetTemplatesAsync(default);
// Process templates...
}
catch (Exception ex)
{
return $"Error accessing template engine: {ex.Message}";
}The server is deployed as a stdio-based MCP server:
- Claude Desktop users add it to their config
- The server communicates via stdin/stdout
- Logging goes to stderr to avoid interfering with MCP messages
Focus on evergreen documentation that provides lasting value to users:
- README.md - Project overview, features, installation, usage
- doc/sdk-integration.md - Technical details about SDK integration
- This file - Development guidelines and conventions
Avoid creating summary documents for Copilot activities or individual changes:
- ? "Changes made in this session"
- ? "Summary of what was implemented"
- ? "List of files modified"
- ? Temporary activity logs
These are unnecessary overhead and become stale quickly. Instead:
- ? Update existing documentation to reflect new features
- ? Add examples and usage patterns
- ? Document architectural decisions
- ? Keep documentation focused on helping users and future developers
When adding features:
- Update README.md if new tools are added
- Update doc/sdk-integration.md if SDK integration changes
- Update this file if new patterns or conventions are introduced
- Do NOT create separate "summary" or "change log" documents
- GitHub Actions runs on push and PR
- Builds in Release configuration
- Runs comprehensive test suite:
- MCP conformance tests (protocol compliance)
- Unit tests with code coverage (Cobertura format)
- Performance smoke tests (informational)
- Uploads coverage to Codecov
- Ensures code compiles and tests pass before merge
CI uploads a Cobertura coverage artifact named coverage-cobertura on each build.yml run.
To download and summarize coverage locally:
pwsh -File scripts/download-coverage-artifact.ps1To diagnose a specific GitHub Actions run:
pwsh -File scripts/download-coverage-artifact.ps1 -RunId <runId>To diagnose a specific pull request:
pwsh -File scripts/download-coverage-artifact.ps1 -PullRequest <prNumber>When using -PullRequest, the script also downloads the latest successful run for the base branch (defaults to -Branch main) and prints a PR-vs-base delta. Use -NoBaseCompare to disable.
Notes:
- Requires GitHub CLI (
gh) and auth (gh auth login). - The script prints overall rates plus lowest-covered files and saves output under
artifacts/coverage/run-<runId>/.
The project uses the following NuGet packages for SDK integration:
Microsoft.TemplateEngine.Abstractions- Template metadataMicrosoft.TemplateEngine.Edge- Template engine APIsMicrosoft.Build.Utilities.Core- MSBuild utilitiesMicrosoft.Build- MSBuild APIs
When adding new SDK integration:
- Add appropriate NuGet package
- Create helper class in separate file
- Add MCP tool methods in DotNetCliTools (following naming convention)
- Update doc/sdk-integration.md if adding new SDK capabilities
- Update README.md to list new tools