diff --git a/.vscode/cspell.json b/.vscode/cspell.json
index fe6ec9df9..cb928b5c9 100644
--- a/.vscode/cspell.json
+++ b/.vscode/cspell.json
@@ -477,6 +477,7 @@
"tfvars",
"timechart",
"timespan",
+ "toolset",
"toolsets",
"uaenorth",
"uksouth",
diff --git a/docs/new-command.md b/docs/new-command.md
index 9b7a3f824..551adb14d 100644
--- a/docs/new-command.md
+++ b/docs/new-command.md
@@ -5,16 +5,18 @@
This document provides a comprehensive guide for implementing commands in Azure MCP following established patterns.
-## Area Pattern: Organizing code by area
+## Toolset Pattern: Organizing code by toolset
-All new Azure services and their commands should use the Area pattern:
+All new Azure services and their commands should use the Toolset pattern:
-- **Area code** goes in `areas/{area-name}/src/AzureMcp.{AreaName}` (e.g., `areas/storage/src/AzureMcp.Storage`)
-- **Tests** go in `areas/{area-name}/tests`, divided into UnitTests and LiveTests:
- - `areas/{area-name}/tests/AzureMcp.{AreaName}.UnitTests`
- - `areas/{area-name}/tests/AzureMcp.{AreaName}.LiveTests`
+- **Toolset code** goes in `tools/Azure.Mcp.Tools.{Toolset}/src` (e.g., `tools/Azure.Mcp.Tools.Storage/src`)
+- **Tests** go in `tools/Azure.Mcp.Tools.{Toolset}/tests`, divided into UnitTests and LiveTests:
+ - `tools/Azure.Mcp.Tools.{Toolset}/tests/Azure.Mcp.Tools.{Toolset}.UnitTests`
+ - `tools/Azure.Mcp.Tools.{Toolset}/tests/Azure.Mcp.Tools.{Toolset}.LiveTests`
+ - `tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.UnitTests`
+ - `tools/Azure.Mcp.Tools.Monitor/tests/Azure.Mcp.Tools.Monitor.LiveTests`
-This keeps all code, options, models, and tests for an area together. See `areas/storage` for a reference implementation.
+This keeps all code, options, models, and tests for an toolset together. See `tools/Azure.Mcp.Tools.Storage` for a reference implementation.
## ⚠️ Test Infrastructure Requirements
@@ -22,11 +24,11 @@ This keeps all code, options, models, and tests for an area together. See `areas
### **Azure Service Commands (REQUIRES Test Infrastructure)**
If your command interacts with Azure resources (storage accounts, databases, VMs, etc.):
-- ✅ **MUST create** `areas/{area-name}/tests/test-resources.bicep`
-- ✅ **MUST create** `areas/{area-name}/tests/test-resources-post.ps1` (required even if basic template)
+- ✅ **MUST create** `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep`
+- ✅ **MUST create** `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources-post.ps1` (required even if basic template)
- ✅ **MUST include** RBAC role assignments for test application
-- ✅ **MUST validate** with `az bicep build --file areas/{area-name}/tests/test-resources.bicep`
-- ✅ **MUST test deployment** with `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name}`
+- ✅ **MUST validate** with `az bicep build --file tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep`
+- ✅ **MUST test deployment** with `./eng/scripts/Deploy-TestResources.ps1 -Tool 'Azure.Mcp.Tools.{Toolset}'`
### **Non-Azure Commands (No Test Infrastructure Needed)**
If your command is a wrapper/utility (CLI tools, best practices, documentation):
@@ -82,8 +84,8 @@ If your command is a wrapper/utility (CLI tools, best practices, documentation):
- `operation`: Action to perform (verb, lowercase)
Each command is:
- - In code, to avoid ambiguity between service classes and Azure services, we refer to Azure services as Areas
- - Registered in the RegisterCommands method of its area's `areas/{area-name}/src/AzureMcp.{AreaName}/{AreaName}Setup.cs` file
+ - In code, to avoid ambiguity between service classes and Azure services, we refer to Azure services as Toolsets
+ - Registered in the RegisterCommands method of its toolset's `tools/Azure.Mcp.Tools.{Toolset}/src/{Toolset}Setup.cs` file
- Organized in a hierarchy of command groups
- Documented with a title, description and examples
- Validated before execution
@@ -107,20 +109,20 @@ If your command is a wrapper/utility (CLI tools, best practices, documentation):
A complete command requires:
-1. OptionDefinitions static class: `areas/{area-name}/src/AzureMcp.{AreaName}/Options/{AreaName}OptionDefinitions.cs`
-2. Options class: `areas/{area-name}/src/AzureMcp.{AreaName}/Options/{Resource}/{Operation}Options.cs`
-3. Command class: `areas/{area-name}/src/AzureMcp.{AreaName}/Commands/{Resource}/{Resource}{Operation}Command.cs`
-4. Service interface: `areas/{area-name}/src/AzureMcp.{AreaName}/Services/I{ServiceName}Service.cs`
-5. Service implementation: `areas/{area-name}/src/AzureMcp.{AreaName}/Services/{ServiceName}Service.cs`
- - It's common for an area to have a single service class named after the
- area but some areas will have multiple service classes
-6. Unit test: `areas/{area-name}/tests/AzureMcp.{AreaName}.UnitTests/{Resource}/{Resource}{Operation}CommandTests.cs`
-7. Integration test: `areas/{area-name}/tests/AzureMcp.{AreaName}.LiveTests/{AreaName}CommandTests.cs`
-8. Command registration in RegisterCommands(): `areas/{area-name}/src/AzureMcp.{AreaName}/{AreaName}Setup.cs`
-9. Area registration in RegisterAreas(): `core/src/AzureMcp.Cli/Program.cs`
+1. OptionDefinitions static class: `tools/Azure.Mcp.Tools.{Toolset}/src/Options/{Toolset}OptionDefinitions.cs`
+2. Options class: `tools/Azure.Mcp.Tools.{Toolset}/src/Options/{Resource}/{Operation}Options.cs`
+3. Command class: `tools/Azure.Mcp.Tools.{Toolset}/src/Commands/{Resource}/{Resource}{Operation}Command.cs`
+4. Service interface: `tools/Azure.Mcp.Tools.{Toolset}/src/Services/I{ServiceName}Service.cs`
+5. Service implementation: `tools/Azure.Mcp.Tools.{Toolset}/src/Services/{ServiceName}Service.cs`
+ - It's common for an toolset to have a single service class named after the
+ toolset but some toolsets will have multiple service classes
+6. Unit test: `tools/Azure.Mcp.Tools.{Toolset}/tests/Azure.Mcp.Tools.{Toolset}.UnitTests/{Resource}/{Resource}{Operation}CommandTests.cs`
+7. Integration test: `tools/Azure.Mcp.Tools.{Toolset}/tests/Azure.Mcp.Tools.{Toolset}.LiveTests/{Toolset}CommandTests.cs`
+8. Command registration in RegisterCommands(): `tools/Azure.Mcp.Tools.{Toolset}/src/{Toolset}Setup.cs`
+9. Toolset registration in RegisterAreas(): `servers/Azure.Mcp.Server/src/Program.cs`
10. **Live test infrastructure** (for Azure service commands):
- - Bicep template: `/areas/{area-name}/tests/test-resources.bicep`
- - Post-deployment script: `/areas/{area-name}/tests/test-resources-post.ps1` (required, even if basic template)
+ - Bicep template: `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep`
+ - Post-deployment script: `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources-post.ps1` (required, even if basic template)
### File and Class Naming Convention
@@ -153,16 +155,16 @@ This convention ensures:
- Better IDE intellisense and code navigation
- Easier maintenance and discovery
-**IMPORTANT**: If implementing a new area, you must also ensure:
+**IMPORTANT**: If implementing a new toolset, you must also ensure:
- The Azure Resource Manager package is added to `Directory.Packages.props` first
- Models, base commands, and option definitions follow the established patterns
- JSON serialization context includes all new model types
-- Service registration in the area setup ConfigureServices method
-- **Live test infrastructure**: Add Bicep template to `/areas/{area-name}/tests`
+- Service registration in the toolset setup ConfigureServices method
+- **Live test infrastructure**: Add Bicep template to `tools/Azure.Mcp.Tools.{Toolset}/tests`
- **Test resource deployment**: Ensure resources are properly configured with RBAC for test application
- **Resource naming**: Follow consistent naming patterns - many services use just `baseName`, while others may need suffixes for disambiguation (e.g., `{baseName}-suffix`)
- **Solution file integration**: Add new projects to `AzureMcp.sln` with proper GUID generation to avoid conflicts
-- **Program.cs registration**: Register the new area in `Program.cs` `RegisterAreas()` method in alphabetical order
+- **Program.cs registration**: Register the new toolset in `Program.cs` `RegisterAreas()` method in alphabetical order
## Implementation Guidelines
@@ -174,13 +176,13 @@ When creating commands that interact with Azure services, you'll need to:
For **Resource Graph queries** (using `BaseAzureResourceService`):
- No additional packages required - `Azure.ResourceManager.ResourceGraph` is already included in the core project
-- Only add area-specific packages if you need direct ARM operations beyond Resource Graph queries
+- Only add toolset-specific packages if you need direct ARM operations beyond Resource Graph queries
- Example: `` (only if needed for direct ARM operations)
For **Direct ARM operations** (using `BaseAzureService`):
- Add the appropriate Azure Resource Manager package to `Directory.Packages.props`
- Example: ``
-- Add the package reference in `AzureMcp.{AreaName}.csproj`
+- Add the package reference in `Azure.Mcp.Tools.{Toolset}.csproj`
- Example: ``
- **Version Consistency**: Ensure the package version in `Directory.Packages.props` matches across all projects
- **Build Order**: Add the package to `Directory.Packages.props` first, then reference it in project files to avoid build errors
@@ -307,7 +309,7 @@ var databaseResource = await sqlServerResource.Value
### 2. Options Class
```csharp
-public class {Resource}{Operation}Options : Base{Area}Options
+public class {Resource}{Operation}Options : Base{Toolset}Options
{
// Only add properties not in base class
public string? NewOption { get; set; }
@@ -315,7 +317,7 @@ public class {Resource}{Operation}Options : Base{Area}Options
```
IMPORTANT:
-- Inherit from appropriate base class (Base{Area}Options, GlobalOptions, etc.)
+- Inherit from appropriate base class (Base{Toolset}Options, GlobalOptions, etc.)
- Never redefine properties from base classes
- Make properties nullable if not required
- Use consistent parameter names across services:
@@ -339,7 +341,7 @@ protected string? GetResourceGroup(); // Convenience accessor with validation
```
Key rules:
-- Do NOT create area-specific optional resource group options.
+- Do NOT create toolset-specific optional resource group options.
- Do NOT override `_resourceGroupOption` or manually add `OptionDefinitions.Common.ResourceGroup` to commands.
- Do NOT manually assign `options.ResourceGroup` in `BindOptions` – central binding in `GlobalCommand` handles this when a command calls either helper.
- Validation for required resource group happens centrally (logical requirement), not at parser level.
@@ -390,13 +392,13 @@ Rationale:
```csharp
public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Command> logger)
- : Base{Area}Command<{Resource}{Operation}Options>
+ : Base{Toolset}Command<{Resource}{Operation}Options>
{
private const string CommandTitle = "Human Readable Title";
private readonly ILogger<{Resource}{Operation}Command> _logger = logger;
// Define options from OptionDefinitions
- private readonly Option _newOption = {Area}OptionDefinitions.NewOption;
+ private readonly Option _newOption = {Toolset}OptionDefinitions.NewOption;
public override string Name => "operation";
@@ -444,7 +446,7 @@ public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Co
context.Activity?.WithSubscriptionTag(options);
// Get the appropriate service from DI
- var service = context.GetService();
+ var service = context.GetService();
// Call service operation(s) with required parameters
var results = await service.{Operation}(
@@ -457,7 +459,7 @@ public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Co
context.Response.Results = results?.Count > 0 ?
ResponseResult.Create(
new {Operation}CommandResult(results),
- {Area}JsonContext.Default.{Operation}CommandResult) :
+ {Toolset}JsonContext.Default.{Operation}CommandResult) :
null;
}
catch (Exception ex)
@@ -495,17 +497,17 @@ public sealed class {Resource}{Operation}Command(ILogger<{Resource}{Operation}Co
### 4. Service Interface and Implementation
-Each area has its own service interface that defines the methods that commands will call. The interface will have an implementation that contains the actual logic.
+Each toolset has its own service interface that defines the methods that commands will call. The interface will have an implementation that contains the actual logic.
```csharp
-public interface IService
+public interface IService
{
...
}
```
```csharp
-public class Service(ISubscriptionService subscriptionService, ITenantService tenantService, ICacheService cacheService) : BaseAzureService(tenantService), IService
+public class Service(ISubscriptionService subscriptionService, ITenantService tenantService, ICacheService cacheService) : BaseAzureService(tenantService), IService
{
...
}
@@ -533,30 +535,30 @@ Task> GetStorageAccounts(string subscription, string? tenant = null
### 5. Base Service Command Classes
-Each area has its own hierarchy of base command classes that inherit from `GlobalCommand` or `SubscriptionCommand`. Service classes that work with Azure resources should inject `ISubscriptionService` for subscription resolution. For example:
+Each toolset has its own hierarchy of base command classes that inherit from `GlobalCommand` or `SubscriptionCommand`. Service classes that work with Azure resources should inject `ISubscriptionService` for subscription resolution. For example:
```csharp
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Diagnostics.CodeAnalysis;
-using AzureMcp.Core.Commands;
-using AzureMcp.Core.Commands.Subscription;
-using AzureMcp.{Area}.Options;
+using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Commands.Subscription;
+using Azure.Mcp.Tools.{Toolset}.Options;
-namespace AzureMcp.{Area}.Commands;
+namespace Azure.Mcp.Tools.{Toolset}.Commands;
// Base command for all service commands (if no members needed, use concise syntax)
-public abstract class Base{Area}Command<
+public abstract class Base{Toolset}Command<
[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions>
- : SubscriptionCommand where TOptions : Base{Area}Options, new();
+ : SubscriptionCommand where TOptions : Base{Toolset}Options, new();
// Base command for all service commands (if members are needed, use full syntax)
-public abstract class Base{Area}Command<
+public abstract class Base{Toolset}Command<
[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] TOptions>
- : SubscriptionCommand where TOptions : Base{Area}Options, new()
+ : SubscriptionCommand where TOptions : Base{Toolset}Options, new()
{
- protected readonly Option _commonOption = {Area}OptionDefinitions.CommonOption;
+ protected readonly Option _commonOption = {Toolset}OptionDefinitions.CommonOption;
protected readonly Option _resourceGroupOption = OptionDefinitions.Common.ResourceGroup;
protected virtual bool RequiresResourceGroup => true;
@@ -587,8 +589,8 @@ public abstract class Base{Area}Command<
}
// Service implementation example with subscription resolution
-public class {Area}Service(ISubscriptionService subscriptionService, ITenantService tenantService)
- : BaseAzureService(tenantService), I{Area}Service
+public class {Toolset}Service(ISubscriptionService subscriptionService, ITenantService tenantService)
+ : BaseAzureService(tenantService), I{Toolset}Service
{
private readonly ISubscriptionService _subscriptionService = subscriptionService ?? throw new ArgumentNullException(nameof(subscriptionService));
@@ -612,7 +614,7 @@ Unit tests follow a standardized pattern that tests initialization, validation,
public class {Resource}{Operation}CommandTests
{
private readonly IServiceProvider _serviceProvider;
- private readonly I{Area}Service _service;
+ private readonly I{Toolset}Service _service;
private readonly ILogger<{Resource}{Operation}Command> _logger;
private readonly {Resource}{Operation}Command _command;
private readonly CommandContext _context;
@@ -620,7 +622,7 @@ public class {Resource}{Operation}CommandTests
public {Resource}{Operation}CommandTests()
{
- _service = Substitute.For();
+ _service = Substitute.For();
_logger = Substitute.For>();
var collection = new ServiceCollection().AddSingleton(_service);
@@ -695,7 +697,7 @@ public class {Resource}{Operation}CommandTests
Integration tests inherit from `CommandTestsBase` and use test fixtures:
```csharp
-public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelper output)
+public class {Toolset}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelper output)
: CommandTestsBase(liveTestFixture, output), IClassFixture
{
[Theory]
@@ -705,7 +707,7 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
{
// Arrange
var result = await CallToolAsync(
- "azmcp_{area}_{resource}_{operation}",
+ "azmcp_{Toolset}_{resource}_{operation}",
new()
{
{ "subscription", Settings.Subscription },
@@ -731,7 +733,7 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
public async Task Should_Return400_WithInvalidInput(string args)
{
var result = await CallToolAsync(
- $"azmcp_{area}_{resource}_{operation} {args}");
+ $"azmcp_{Toolset}_{resource}_{operation} {args}");
Assert.Equal(400, result.GetProperty("status").GetInt32());
Assert.Contains("required",
@@ -746,8 +748,8 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
private void RegisterCommands(CommandGroup rootGroup, ILoggerFactory loggerFactory)
{
var service = new CommandGroup(
- "{area}",
- "{Area} operations");
+ "{Toolset}",
+ "{Toolset} operations");
rootGroup.AddSubGroup(service);
var resource = new CommandGroup(
@@ -764,23 +766,23 @@ private void RegisterCommands(CommandGroup rootGroup, ILoggerFactory loggerFacto
- ✅ Good: `"entraadmin"`, `"resourcegroup"`, `"storageaccount"`, `"entra-admin"`
- ❌ Bad: `"entra_admin"`, `"resource_group"`, `"storage_account"`
-### 9. Area Registration
+### 9. Toolset Registration
```csharp
- private static IAreaSetup[] RegisterAreas()
+ private static IToolsetSetup[] RegisterAreas()
{
return [
- // Register core areas
- new AzureMcp.AzureBestPractices.AzureBestPracticesSetup(),
- new AzureMcp.Extension.ExtensionSetup(),
+ // Register core toolsets
+ new Azure.Mcp.Tools.AzureBestPractices.AzureBestPracticesSetup(),
+ new Azure.Mcp.Tools.Extension.ExtensionSetup(),
- // Register Azure service areas
- new AzureMcp.{Area}.{Area}Setup(),
- new AzureMcp.Storage.StorageSetup(),
+ // Register Azure service toolsets
+ new Azure.Mcp.Tools.{Toolset}.{Toolset}Setup(),
+ new Azure.Mcp.Tools.Storage.StorageSetup(),
];
}
```
-The area list in `RegisterAreas()` should stay sorted alphabetically.
+The toolset list in `RegisterAreas()` should stay sorted alphabetically.
## Error Handling
@@ -937,7 +939,7 @@ public async Task ExecuteAsync_HandlesServiceError()
When developing new commands, run only your specific tests to save time:
```bash
# Run all tests from the test project directory:
-pushd ./areas/your-area/tests/AzureMcp.YourArea.UnitTests #or .LiveTests
+pushd ./tools/Azure.Mcp.Tools.YourToolset/tests/Azure.Mcp.Tools.YourToolset.UnitTests #or .LiveTests
# Run only tests for your specific command class
dotnet test --filter "FullyQualifiedName~YourCommandNameTests" --verbosity normal
@@ -945,18 +947,18 @@ dotnet test --filter "FullyQualifiedName~YourCommandNameTests" --verbosity norma
# Example: Run only SQL AD Admin tests
dotnet test --filter "FullyQualifiedName~EntraAdminListCommandTests" --verbosity normal
-# Run all tests for a specific area
+# Run all tests for a specific toolset
dotnet test --verbosity normal
```
### Integration Tests
-Azure service commands requiring test resource deployment must add a bicep template, `tests/test-resources.bicep`, to their area directory. Additionally, all Azure service commands must include a `test-resources-post.ps1` file in the same directory, even if it contains only the basic template without custom logic. See `/areas/storage/tests/test-resources.bicep` and `/areas/storage/tests/test-resources-post.ps1` for canonical examples.
+Azure service commands requiring test resource deployment must add a bicep template, `tests/test-resources.bicep`, to their toolset directory. Additionally, all Azure service commands must include a `test-resources-post.ps1` file in the same directory, even if it contains only the basic template without custom logic. See `/tools/Azure.Mcp.Tools.Storage/tests/test-resources.bicep` and `/tools/Azure.Mcp.Tools.Storage/tests/test-resources-post.ps1` for canonical examples.
#### Live Test Resource Infrastructure
-**1. Create Area Bicep Template (`/areas/{area-name}/tests/test-resources.bicep`)**
+**1. Create Toolset Bicep Template (`/tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep`)**
-Follow this pattern for your area's infrastructure:
+Follow this pattern for your toolset's infrastructure:
```bicep
targetScope = 'resourceGroup'
@@ -1031,14 +1033,14 @@ output testResourceName string = serviceResource::testResource.name
- Use resource naming that clearly identifies test purposes
**Common Resource Naming Patterns:**
-- Deployments are on a per-area basis. Name collisions should not occur across area templates.
+- Deployments are on a per-toolset basis. Name collisions should not occur across toolset templates.
- Main service: `baseName` (most common, e.g., `mcp12345`) or `{baseName}{suffix}` if disambiguation needed
- Child resources: `test{resource}` (e.g., `testdb`, `testcontainer`)
- Follow Azure naming conventions and length limits
- Ensure names are unique within resource group scope
- Check existing `test-resources.bicep` files for consistent patterns
-**2. Required: Post-Deployment Script (`/areas/{area-name}/tests/test-resources-post.ps1`)**
+**2. Required: Post-Deployment Script (`tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources-post.ps1`)**
All Azure service commands must include this script, even if it contains only the basic template. Create with the standard template and add custom setup logic if needed:
@@ -1060,11 +1062,11 @@ param (
[hashtable] $AdditionalParameters
)
-Write-Host "Running {Area} post-deployment setup..."
+Write-Host "Running {Toolset} post-deployment setup..."
try {
# Extract outputs from deployment
- $serviceName = $DeploymentOutputs['{area}']['serviceResourceName']['value']
+ $serviceName = $DeploymentOutputs['{Toolset}']['serviceResourceName']['value']
$resourceGroup = $AdditionalParameters['ResourceGroupName']
# Perform additional setup (e.g., create sample data, configure settings)
@@ -1073,10 +1075,10 @@ try {
# Example: Run Azure CLI commands for additional setup
# az {service} {operation} --name $serviceName --resource-group $resourceGroup
- Write-Host "{Area} post-deployment setup completed successfully."
+ Write-Host "{Toolset} post-deployment setup completed successfully."
}
catch {
- Write-Error "Failed to complete {Area} post-deployment setup: $_"
+ Write-Error "Failed to complete {Toolset} post-deployment setup: $_"
throw
}
```
@@ -1086,9 +1088,9 @@ catch {
Integration tests should use the deployed infrastructure:
```csharp
-[Trait("Area", "{Area}")]
+[Trait("Toolset", "{Toolset}")]
[Trait("Category", "Live")]
-public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelper output)
+public class {Toolset}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelper output)
: CommandTestsBase(liveTestFixture, output), IClassFixture
{
[Fact]
@@ -1099,7 +1101,7 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
var resourceName = "test{resource}";
var result = await CallToolAsync(
- "azmcp_{area}_{resource}_show",
+ "azmcp_{Toolset}_{resource}_show",
new()
{
{ "subscription", Settings.SubscriptionId },
@@ -1127,7 +1129,7 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
var argsString = string.Join(" ", allArgs);
var result = await CallToolAsync(
- "azmcp_{area}_{resource}_show",
+ "azmcp_{Toolset}_{resource}_show",
new()
{
{ "args", argsString }
@@ -1141,14 +1143,15 @@ public class {Area}CommandTests(LiveTestFixture liveTestFixture, ITestOutputHelp
**5. Deploy and Test Resources**
-Use the deployment script with your area:
+Use the deployment script with your toolset:
```powershell
-# Deploy test resources for your area
-./eng/scripts/Deploy-TestResources.ps1 -Areas "{Area}"
+# Deploy test resources for your toolset
+./eng/scripts/Deploy-TestResources.ps1 -Tools "{Toolset}"
# Run live tests
-dotnet test --filter "Category=Live&Area={Area}"
+pushd 'tools/Azure.Mcp.Tools.{Toolset}/tests/Azure.Mcp.Tools.{Toolset}.LiveTests'
+dotnet test
```
Live test scenarios should include:
@@ -1205,7 +1208,7 @@ When creating new C# files, start with only the using statements you actually ne
```csharp
// Start minimal - only add what you actually use
-using AzureMcp.Core.Commands;
+using Azure.Mcp.Core.Commands;
using Microsoft.Extensions.Logging;
// Add more using statements as you implement the code
@@ -1232,8 +1235,8 @@ The project already has `enable` in `Directory.
Use these commands to detect and remove unused using statements:
```powershell
-# Format specific area files (recommended during development)
-dotnet format --include="areas/{area-name}/**/*.cs" --verbosity normal
+# Format specific toolset files (recommended during development)
+dotnet format --include="tools/Azure.Mcp.Tools.{Toolset}/**/*.cs" --verbosity normal
# Format entire solution (use sparingly - takes longer)
dotnet format ./AzureMcp.sln --verbosity normal
@@ -1249,17 +1252,17 @@ dotnet build --verbosity normal | Select-String "warning"
// Copied from another file but not all are needed
using System.CommandLine;
using System.CommandLine.Parsing;
-using AzureMcp.Acr.Commands; // ← May not be needed
-using AzureMcp.Acr.Options; // ← May not be needed
-using AzureMcp.Acr.Options.Registry; // ← May not be needed
-using AzureMcp.Acr.Services;
+using Azure.Mcp.Tools.Acr.Commands; // ← May not be needed
+using Azure.Mcp.Tools.Acr.Options; // ← May not be needed
+using Azure.Mcp.Tools.Acr.Options.Registry; // ← May not be needed
+using Azure.Mcp.Tools.Acr.Services;
// ... 15 more using statements
```
✅ **Start minimal and add as needed:**
```csharp
// Only what's actually used in this file
-using AzureMcp.Acr.Services;
+using Azure.Mcp.Tools.Acr.Services;
using Microsoft.Extensions.Logging;
```
@@ -1283,7 +1286,7 @@ The project checklist already includes cleaning up unused using statements:
**Make this part of your development workflow:**
1. Write code with minimal using statements
2. Add using statements only as you need them
-3. Run `dotnet format --include="areas/{area-name}/**/*.cs"` before committing
+3. Run `dotnet format --include="tools/Azure.Mcp.Tools.{Toolset}/**/*.cs"` before committing
4. Use IDE features to clean up automatically
### Build Verification and AOT Compatibility
@@ -1301,35 +1304,34 @@ dotnet test --filter "FullyQualifiedName~YourCommandTests"
**2. AOT Compilation Verification:**
-AOT (Ahead-of-Time) compilation is required for all new areas to ensure compatibility with native builds:
+AOT (Ahead-of-Time) compilation is required for all new toolsets to ensure compatibility with native builds:
```powershell
-# Test AOT compatibility - this is REQUIRED for all new areas
+# Test AOT compatibility - this is REQUIRED for all new toolsets
./eng/scripts/Build-Local.ps1 -BuildNative
```
-**Expected Outcome**: If your area is properly implemented, the build should succeed. However, if AOT compilation fails (which is very likely for new areas), follow these steps:
-
+**Expected Outcome**: If your toolset is properly implemented, the build should succeed. However, if AOT compilation fails (which is very likely for new toolsets), follow these steps:
**3. AOT Compilation Issue Resolution:**
-When AOT compilation fails for your new area, you need to exclude it from native builds:
+When AOT compilation fails for your new toolset, you need to exclude it from native builds:
-**Step 1: Move area setup under BuildNative condition in Program.cs**
+**Step 1: Move toolset setup under BuildNative condition in Program.cs**
```csharp
-// Find your area setup call in Program.cs
+// Find your toolset setup call in Program.cs
// Move it inside the #if !BUILD_NATIVE block
#if !BUILD_NATIVE
- // ... other area setups ...
- builder.Services.Add{YourArea}Setup(); // ← Move this line here
+ // ... other toolset setups ...
+ builder.Services.Add{YourToolset}Setup(); // ← Move this line here
#endif
```
-**Step 2: Add ProjectReference-Remove condition in AzureMcp.Cli.csproj**
+**Step 2: Add ProjectReference-Remove condition in Azure.Mcp.Server.csproj**
```xml
-
+
-
+
```
@@ -1348,7 +1350,7 @@ dotnet build
- Third-party dependencies that don't support AOT
- Dynamic JSON serialization without source generators
-**Important**: This is a common and expected issue for new Azure service areas. The exclusion pattern is the standard solution and doesn't impact regular builds or functionality.
+**Important**: This is a common and expected issue for new Azure service toolsets. The exclusion pattern is the standard solution and doesn't impact regular builds or functionality.
## Common Implementation Issues and Solutions
@@ -1525,7 +1527,7 @@ catch (Exception ex)
6. Live Test Infrastructure:
- Use minimal resource configurations for cost efficiency
- - Follow naming conventions: `baseName` (most common) or `{baseName}-{area}` if needed
+ - Follow naming conventions: `baseName` (most common) or `{baseName}-{Toolset}` if needed
- Include proper RBAC assignments for test application
- Output all necessary identifiers for test consumption
- Use appropriate Azure service API versions
@@ -1551,14 +1553,14 @@ catch (Exception ex)
- Use dashes in command group names
2. Always:
- - Create a static {Area}OptionDefinitions class for the area
+ - Create a static {Toolset}OptionDefinitions class for the toolset
- **For resource group handling**: Call `UseResourceGroup()` (optional) or `RequireResourceGroup()` (required). Never redefine the option or assign it manually.
- **For Azure service commands**: Create test infrastructure (`test-resources.bicep`) before implementing live tests
- Use OptionDefinitions for options
- Follow exact file structure
- Implement all base members
- Add both unit and integration tests
- - Register in area setup RegisterCommands method
+ - Register in toolset setup RegisterCommands method
- Handle all error cases
- Use primary constructors
- Make command classes sealed
@@ -1588,14 +1590,14 @@ catch (Exception ex)
**Issue: Missing live test infrastructure for Azure service commands**
- **Cause**: Forgetting to create `test-resources.bicep` template during development
- **Solution**: Create Bicep template early in development process, not as an afterthought
-- **Fix**: Create `areas/{area-name}/tests/test-resources.bicep` following established patterns
+- **Fix**: Create `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep` following established patterns
- **Prevention**: Check "Test Infrastructure Requirements" section at top of this document before starting implementation
-- **Validation**: Run `az bicep build --file areas/{area-name}/tests/test-resources.bicep` to validate template
+- **Validation**: Run `az bicep build --file tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep` to validate template
**Issue: Pipeline fails with "SelfContainedPostScript is not supported if there is no test-resources-post.ps1"**
- **Cause**: Missing required `test-resources-post.ps1` file for Azure service commands
- **Solution**: Create the post-deployment script file, even if it contains only the basic template
-- **Fix**: Create `areas/{area-name}/tests/test-resources-post.ps1` using the standard template from existing areas
+- **Fix**: Create `tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources-post.ps1` using the standard template from existing toolsets
- **Prevention**: All Azure service commands must include this file - it's required by the test infrastructure
- **Note**: The file is mandatory even if no custom post-deployment logic is needed
@@ -1605,7 +1607,7 @@ catch (Exception ex)
- `using System.Text.Json;` for JSON serialization
- `using Xunit;` for test framework
- `using NSubstitute;` for mocking
- - `using AzureMcp.Tests;` for test base classes
+ - `using Azure.Mcp.Tests;` for test base classes
- **Fix**: Review test project template and ensure all necessary imports are included
- **Prevention**: Use existing test projects as templates for import statements
@@ -1651,7 +1653,7 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
**Issue: Bicep template validation fails**
- **Cause**: Invalid parameter constraints, missing required properties, or API version issues
-- **Solution**: Use `az bicep build --file areas/{area-name}/tests/test-resources.bicep` to validate template
+- **Solution**: Use `az bicep build --file tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep` to validate template
- **Fix**: Check Azure Resource Manager template reference for correct syntax and required properties
**Issue: Live tests fail with "Resource not found"**
@@ -1668,14 +1670,14 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
- **Cause**: Parameter constraints, resource naming conflicts, or invalid configurations
- **Solution**:
- Review deployment logs and error messages
- - Use `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name} -Debug` for verbose deployment logs including resource provider errors.
+ - Use `./eng/scripts/Deploy-TestResources.ps1 -Toolset {Toolset} -Debug` for verbose deployment logs including resource provider errors.
### Live Test Project Configuration Issues
**Issue: Live tests fail with "MCP server process exited unexpectedly" and "azmcp.exe not found"**
-- **Cause**: Incorrect project configuration in `AzureMcp.{Area}.LiveTests.csproj`
-- **Common Problem**: Referencing the area project (`AzureMcp.{Area}`) instead of the CLI project
-- **Solution**: Live test projects must reference `AzureMcp.Cli.csproj` and include specific project properties
+- **Cause**: Incorrect project configuration in `Azure.Mcp.Tools.{Toolset}.LiveTests.csproj`
+- **Common Problem**: Referencing the toolset project (`Azure.Mcp.Tools.{Toolset}`) instead of the CLI project
+- **Solution**: Live test projects must reference `Azure.Mcp.Server.csproj` and include specific project properties
- **Required Configuration**:
```xml
@@ -1689,16 +1691,16 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
-
-
+
+
```
- **Key Requirements**:
- `OutputType=Exe` - Required for live test execution
- `IsTestProject=true` - Marks as test project
- - Reference to `AzureMcp.Cli.csproj` - Provides the executable for MCP server
- - Reference to area project - Provides the commands to test
+ - Reference to `Azure.Mcp.Server.csproj` - Provides the executable for MCP server
+ - Reference to toolset project - Provides the commands to test
- **Common fixes**:
- Adjust `@minLength`/`@maxLength` for service naming limits
- Ensure unique resource names within scope
@@ -1717,26 +1719,26 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
### Service Implementation Issues
**Issue: JSON Serialization Context missing new types**
-- **Cause**: New model classes not included in `{Area}JsonContext` causing serialization failures
+- **Cause**: New model classes not included in `{Toolset}JsonContext` causing serialization failures
- **Solution**: Add all new model types to the JSON serialization context
-- **Fix**: Update `{Area}JsonContext.cs` to include `[JsonSerializable(typeof(NewModelType))]` attributes
+- **Fix**: Update `{Toolset}JsonContext.cs` to include `[JsonSerializable(typeof(NewModelType))]` attributes
- **Prevention**: Always update JSON context when adding new model classes
-**Issue: Area not registered in Program.cs**
-- **Cause**: New area setup not added to `RegisterAreas()` method in `Program.cs`
-- **Solution**: Add area registration to the array in alphabetical order
-- **Fix**: Add `new AzureMcp.{Area}.{Area}Setup(),` to the `RegisterAreas()` return array
-- **Prevention**: Follow the complete area setup checklist including Program.cs registration
+**Issue: Toolset not registered in Program.cs**
+- **Cause**: New toolset setup not added to `RegisterAreas()` method in `Program.cs`
+- **Solution**: Add toolset registration to the array in alphabetical order
+- **Fix**: Add `new Azure.Mcp.Tools.{Toolset}.{Toolset}Setup(),` to the `RegisterAreas()` return array
+- **Prevention**: Follow the complete toolset setup checklist including Program.cs registration
**Issue: Using required ResourceGroup option for optional filtering**
- **Cause**: Using `OptionDefinitions.Common.ResourceGroup` which has `IsRequired = true` for commands that should support optional resource group filtering
-- **Solution**: Create custom optional resource group option in area's OptionDefinitions
+- **Solution**: Create custom optional resource group option in toolset's OptionDefinitions
- **Fix**:
- 1. Add `OptionalResourceGroup` option with `IsRequired = false` to `{Area}OptionDefinitions.cs`
+ 1. Add `OptionalResourceGroup` option with `IsRequired = false` to `{Toolset}OptionDefinitions.cs`
2. Override base `_resourceGroupOption` field with `new` keyword in command class
- 3. Use the pattern: `private readonly new Option _resourceGroupOption = {Area}OptionDefinitions.OptionalResourceGroup;`
+ 3. Use the pattern: `private readonly new Option _resourceGroupOption = {Toolset}OptionDefinitions.OptionalResourceGroup;`
- **Prevention**: Check if resource group should be optional (e.g., for list commands) and use the optional pattern
-- **Examples**: Extension (AZQR), Monitor (Metrics), and ACR areas all implement this pattern correctly
+- **Examples**: Extension (AZQR), Monitor (Metrics), and ACR toolsets all implement this pattern correctly
**Issue: HandleException parameter mismatch**
- **Cause**: Confusion about the correct HandleException signature
@@ -1748,7 +1750,7 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
- **Solution**: Add `context.Activity?.WithSubscriptionTag(options);` or use `AddSubscriptionInformation(context.Activity, options);`
**Issue: Service not registered in DI**
-- **Cause**: Forgot to register service in area setup
+- **Cause**: Forgot to register service in toolset setup
- **Solution**: Add `services.AddSingleton();` in ConfigureServices
### Base Command Class Issues
@@ -1758,7 +1760,7 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
- **Solution**: Use correct generic type: `ILogger>`
**Issue: Missing using statements for TrimAnnotations**
-- **Solution**: Add `using AzureMcp.Core.Commands;` for `TrimAnnotations.CommandAnnotations`
+- **Solution**: Add `using Azure.Mcp.Core.Commands;` for `TrimAnnotations.CommandAnnotations`
### AOT Compilation Issues
@@ -1767,16 +1769,16 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
- **Symptoms**: Build errors when running `./eng/scripts/Build-Local.ps1 -BuildNative`
- **Solution**: Exclude non-AOT safe projects and packages for native builds
- **Fix Steps**:
- 1. **Move area setup under conditional compilation** in `core/src/AzureMcp.Cli/Program.cs`:
+ 1. **Move toolset setup under conditional compilation** in `servers/Azure.Mcp.Server/src/Program.cs`:
```csharp
#if !BUILD_NATIVE
- new AzureMcp.{Area}.{Area}Setup(),
+ new Azure.Mcp.Tools.{Toolset}.{Toolset}Setup(),
#endif
```
- 2. **Add conditional project exclusion** in `core/src/AzureMcp.Cli/AzureMcp.Cli.csproj`:
+ 2. **Add conditional project exclusion** in `servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj`:
```xml
-
+
```
3. **Remove problematic package references** when building native (if applicable):
@@ -1785,9 +1787,9 @@ var subscriptionResource = await _subscriptionService.GetSubscription(subscripti
```
-- **Examples**: See Cosmos, Monitor, Postgres, Search, VirtualDesktop, and BicepSchema areas in Program.cs and AzureMcp.Cli.csproj
-- **Prevention**: Test AOT compilation early in development using `./eng/scripts/Build-Local.ps1 -BuildNative`
-- **Note**: Areas excluded from AOT builds are still available in regular builds and deployments
+- **Examples**: See Cosmos, Monitor, Postgres, Search, VirtualDesktop, and BicepSchema toolsets in Program.cs and Azure.Mcp.Server.csproj
+-**Prevention**: Test AOT compilation early in development using `./eng/scripts/Build-Local.ps1 -BuildNative`
+-**Note**: Toolsets excluded from AOT builds are still available in regular builds and deployments
## Checklist
@@ -1800,7 +1802,7 @@ Before submitting:
- [ ] Service interface and implementation complete
- [ ] Unit tests cover all paths
- [ ] Integration tests added
-- [ ] Command registered in area setup RegisterCommands method
+- [ ] Command registered in toolset setup RegisterCommands method
- [ ] Follows file structure exactly
- [ ] Error handling implemented
- [ ] Documentation complete
@@ -1809,13 +1811,13 @@ Before submitting:
**⚠️ MANDATORY for any command that interacts with Azure resources:**
-- [ ] **Live test infrastructure created** (`test-resources.bicep` template in `areas/{area-name}/tests`)
-- [ ] **Post-deployment script created** (`test-resources-post.ps1` in `areas/{area-name}/tests` - required even if basic template)
-- [ ] **Bicep template validated** with `az bicep build --file areas/{area-name}/tests/test-resources.bicep`
-- [ ] **Live test resource template tested** with `./eng/scripts/Deploy-TestResources.ps1 -Area {area-name}`
+- [ ] **Live test infrastructure created** (`test-resources.bicep` template in `tools/Azure.Mcp.Tools.{Toolset}/tests`)
+- [ ] **Post-deployment script created** (`test-resources-post.ps1` in `tools/Azure.Mcp.Tools.{Toolset}/tests` - required even if basic template)
+- [ ] **Bicep template validated** with `az bicep build --file tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep`
+- [ ] **Live test resource template tested** with `./eng/scripts/Deploy-TestResources.ps1 -Toolset {Toolset}`
- [ ] **RBAC permissions configured** for test application in Bicep template (use appropriate built-in roles)
- [ ] **Live test project configuration correct**:
- - [ ] References `AzureMcp.Cli.csproj` (not just the area project)
+ - [ ] References `Azure.Mcp.Server.csproj` (not just the toolset project)
- [ ] Includes `OutputType=Exe` property
- [ ] Includes `IsTestProject=true` property
- [ ] **Live tests use deployed resources** via `Settings.ResourceBaseName` pattern
@@ -1825,10 +1827,10 @@ Before submitting:
**Skip this section ONLY if your command does not interact with Azure resources (e.g., CLI wrappers, best practices tools).**
### Package and Project Setup
-- [ ] Azure Resource Manager package added to both `Directory.Packages.props` and `AzureMcp.{Area}.csproj`
+- [ ] Azure Resource Manager package added to both `Directory.Packages.props` and `Azure.Mcp.Tools.{Toolset}.csproj`
- [ ] **Package version consistency**: Same version used in both `Directory.Packages.props` and project references
- [ ] **Solution file integration**: Projects added to `AzureMcp.sln` with unique GUIDs (no GUID conflicts)
-- [ ] **Area registration**: Added to `Program.cs` `RegisterAreas()` method in alphabetical order
+- [ ] **Toolset registration**: Added to `Program.cs` `RegisterAreas()` method in alphabetical order
- [ ] JSON serialization context includes all new model types
### Build and Code Quality
@@ -1838,7 +1840,7 @@ Before submitting:
- [ ] Code formatting applied with `dotnet format`
- [ ] Spelling check passes with `.\eng\common\spelling\Invoke-Cspell.ps1`
- [ ] **AOT compilation verified** with `./eng/scripts/Build-Local.ps1 -BuildNative`
-- [ ] **Clean up unused using statements**: Run `dotnet format --include="areas/{area-name}/**/*.cs"` to remove unnecessary imports and ensure consistent formatting
+- [ ] **Clean up unused using statements**: Run `dotnet format --include="tools/Azure.Mcp.Tools.{Toolset}/**/*.cs"` to remove unnecessary imports and ensure consistent formatting
- [ ] Fix formatting issues with `dotnet format ./AzureMcp.sln` and ensure no warnings
- [ ] Identify unused properties for Azure Resource with `.\eng\scripts\Check-Unused-ResourceProperties.ps1`
@@ -1854,15 +1856,15 @@ Before submitting:
- [ ] **CHANGELOG.md**: Add entry under "Unreleased" section describing the new command(s)
- [ ] **docs/azmcp-commands.md**: Add command documentation with description, syntax, parameters, and examples
-- [ ] **README.md**: Update the supported services table and add example prompts demonstrating the new command(s) in the appropriate area section
-- [ ] **eng/vscode/README.md**: Update the VSIX README with new service area (if applicable) and add sample prompts to showcase new command capabilities
+- [ ] **README.md**: Update the supported services table and add example prompts demonstrating the new command(s) in the appropriate toolset section
+- [ ] **eng/vscode/README.md**: Update the VSIX README with new service toolset (if applicable) and add sample prompts to showcase new command capabilities
- [ ] **docs/e2eTestPrompts.md**: Add test prompts for end-to-end validation of the new command(s)
-- [ ] **.github/CODEOWNERS**: Add new area to CODEOWNERS file for proper ownership and review assignments
+- [ ] **.github/CODEOWNERS**: Add new toolset to CODEOWNERS file for proper ownership and review assignments
**Documentation Standards**:
- Use consistent command paths in all documentation (e.g., `azmcp sql db show`, not `azmcp sql database show`)
- Organize example prompts by service in README.md under service-specific sections (e.g., `### 🗄️ Azure SQL Database`)
-- Place new commands in the appropriate area section, or create a new area section if needed
+- Place new commands in the appropriate toolset section, or create a new toolset section if needed
- Provide clear, actionable examples that users can run with placeholder values
- Include parameter descriptions and required vs optional indicators in azmcp-commands.md
- Keep CHANGELOG.md entries concise but descriptive of the capability added