Add Spot VM placement scores tool to Azure Compute#2553
Add Spot VM placement scores tool to Azure Compute#2553haagha wants to merge 5 commits intomicrosoft:mainfrom
Conversation
…ality - Introduced `Azure.Mcp.Tools.ComputeRecommender` project with necessary files. - Implemented `BaseComputeRecommenderCommand` for command structure. - Created commands for fetching Spot Placement Metadata and generating Spot Placement Scores. - Developed service layer to interact with Azure Compute Recommender API. - Added models for handling placement score and metadata information. - Defined options for command parameters including location, desired sizes, and count. - Implemented JSON serialization context for command results. - Added unit tests for command execution and service interactions. - Configured dependency injection for services and commands.
- Removed the ComputeRecommenderService and IComputeRecommenderService interfaces. - Deleted associated unit tests for the removed services. - Introduced new ComputePlacementService to handle spot placement metadata and scores. - Created new command classes: SpotPlacementMetadataCommand and SpotPlacementScoreCommand. - Added models for SpotPlacementMetadataInfo and PlacementScoreInfo. - Implemented option classes for command parameters. - Established a new JSON context for serialization of command results. - Updated command registration and execution logic to align with new service structure.
…ommendation # Conflicts: # .gitignore # servers/Azure.Mcp.Server/docs/azmcp-commands.md # servers/Azure.Mcp.Server/docs/e2eTestPrompts.md # tools/Azure.Mcp.Tools.Compute/src/Azure.Mcp.Tools.Compute.csproj # tools/Azure.Mcp.Tools.Compute/src/ComputeSetup.cs # tools/Azure.Mcp.Tools.Compute/tests/Azure.Mcp.Tools.Compute.LiveTests/ComputeCommandTests.cs
There was a problem hiding this comment.
Pull request overview
Adds a new Azure Compute tool subgroup (compute placementscore spot) that exposes the Azure Spot Placement Scores API, enabling agents to query supported resource types and generate capacity-aware placement scores (with quota hints) across regions/zones.
Changes:
- Introduces Spot placement score commands (
get,generate) with new options/models and a backing ARM-based placement service. - Registers the new tool group in
ComputeSetupand wires up DI + System.Text.Json source-gen contexts for AOT-safe serialization. - Adds unit tests + live tests, and updates server docs, changelog entry, and package dependencies.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/Azure.Mcp.Tools.Compute/src/Services/IComputePlacementService.cs | New service abstraction for metadata + score generation. |
| tools/Azure.Mcp.Tools.Compute/src/Services/ComputePlacementService.cs | ARM implementation using Compute Recommender diagnostic resource. |
| tools/Azure.Mcp.Tools.Compute/src/Commands/PlacementScore/SpotPlacementMetadataCommand.cs | New spot get command surface. |
| tools/Azure.Mcp.Tools.Compute/src/Commands/PlacementScore/SpotPlacementScoreCommand.cs | New spot generate command surface. |
| tools/Azure.Mcp.Tools.Compute/src/Commands/BaseComputePlacementCommand.cs | Shared base command for placement-score options (location + subscription). |
| tools/Azure.Mcp.Tools.Compute/src/Commands/ComputePlacementJsonContext.cs | Source-gen JSON context for placement-score response types. |
| tools/Azure.Mcp.Tools.Compute/src/Options/PlacementScore/*.cs | New option definitions + bound option models. |
| tools/Azure.Mcp.Tools.Compute/src/Models/*.cs | Response models for metadata + individual placement score items. |
| tools/Azure.Mcp.Tools.Compute/src/ComputeSetup.cs | Registers the new subgroup + commands + service in the Compute tool. |
| tools/Azure.Mcp.Tools.Compute/src/Azure.Mcp.Tools.Compute.csproj | Adds Compute.Recommender ARM package dependency. |
| Directory.Packages.props | Adds central package version for Azure.ResourceManager.Compute.Recommender. |
| tools/Azure.Mcp.Tools.Compute/tests/Azure.Mcp.Tools.Compute.UnitTests/PlacementScore/*.cs | Unit tests for new commands. |
| tools/Azure.Mcp.Tools.Compute/tests/Azure.Mcp.Tools.Compute.LiveTests/ComputeCommandTests.cs | Live tests for spot get and spot generate. |
| servers/Azure.Mcp.Server/docs/azmcp-commands.md | Documents the new azmcp compute placementscore spot commands. |
| servers/Azure.Mcp.Server/docs/e2eTestPrompts.md | Adds e2e prompts for the new tools. |
| servers/Azure.Mcp.Server/changelog-entries/haagha-compute-spot-placement-scores.yaml | Changelog entry for the new tool group. |
| ILogger<ComputePlacementService> logger) | ||
| : BaseAzureService(tenantService), IComputePlacementService | ||
| { | ||
| private readonly ISubscriptionService _subscriptionService = subscriptionService; | ||
| private readonly ILogger<ComputePlacementService> _logger = logger; | ||
|
|
There was a problem hiding this comment.
_logger is assigned but never used in this service, which will produce a compiler warning (and the repo treats warnings as errors). Either remove the field/constructor parameter or use the logger (e.g., log at least debug-level details around ARM calls / results).
| public static readonly Option<int> DesiredCount = new($"--{DesiredCountName}") | ||
| { | ||
| Description = "Number of VMs to check placement feasibility for. Must be between 1 and 1000. Defaults to 1.", | ||
| Required = false, | ||
| DefaultValueFactory = _ => 1 | ||
| }; |
There was a problem hiding this comment.
The option description says --desired-count must be between 1 and 1000, but there is no validator enforcing that range. Add command/option validation so invalid values are rejected locally (instead of sending a bad request to ARM and surfacing a less predictable error).
jongio
left a comment
There was a problem hiding this comment.
CI is broken due to the unused _logger field in ComputePlacementService.cs (CS0414 becomes an error with TreatWarningsAsErrors). The Copilot bot already flagged this - either remove it or add logging calls to the service methods.
One additional note: the command IDs (a1b2c3d4-e5f6-7890-abcd-ef1234567890 and b2c3d4e5-f6a7-8901-bcde-f12345678901) look like placeholder values from documentation examples. Consider generating actual UUIDs ([System.Guid]::NewGuid()) to avoid confusion about whether these were meant to be replaced.
On the changelog: the bot flagged Features Added as invalid, but per the schema at eng/schemas/changelog-entry.schema.json, it's a valid section name. No change needed there.
| | Parameter | Required | Description | | ||
| |-----------|----------|-------------| | ||
| | `--subscription` | Yes | Azure subscription ID or name (requires at least Reader role) | | ||
| | `--location` | Yes | ARM region used for API routing (e.g., `eastus`, `westus2`) | | ||
| | `--desired-locations` | Yes (generate) | One or more ARM regions to evaluate (1–3 recommended) | | ||
| | `--desired-sizes` | Yes (generate) | One or more VM SKU names (e.g., `Standard_D2_v2`, `Standard_D4s_v3`) | | ||
| | `--desired-count` | No | Number of VMs to evaluate (1–1000). Defaults to `1` | | ||
| | `--availability-zones` | No | Whether to include zone-level scores. Defaults to `true` | |
There was a problem hiding this comment.
The requiredness should be inferred from the tool definition in the bash section. Let's removed the Required column as it makes the definitions more confusing have it state things like "Required but only for one of the tools mentioned here".
| | Parameter | Required | Description | | |
| |-----------|----------|-------------| | |
| | `--subscription` | Yes | Azure subscription ID or name (requires at least Reader role) | | |
| | `--location` | Yes | ARM region used for API routing (e.g., `eastus`, `westus2`) | | |
| | `--desired-locations` | Yes (generate) | One or more ARM regions to evaluate (1–3 recommended) | | |
| | `--desired-sizes` | Yes (generate) | One or more VM SKU names (e.g., `Standard_D2_v2`, `Standard_D4s_v3`) | | |
| | `--desired-count` | No | Number of VMs to evaluate (1–1000). Defaults to `1` | | |
| | `--availability-zones` | No | Whether to include zone-level scores. Defaults to `true` | | |
| | Parameter | Description | | |
| |-----------|-------------| | |
| | `--subscription` | Azure subscription ID or name (requires at least Reader role) | | |
| | `--location` | ARM region used for API routing (e.g., `eastus`, `westus2`) | | |
| | `--desired-locations` | One or more ARM regions to evaluate (1–3 recommended) | | |
| | `--desired-sizes` | One or more VM SKU names (e.g., `Standard_D2_v2`, `Standard_D4s_v3`) | | |
| | `--desired-count` | Number of VMs to evaluate (1–1000). Defaults to `1` | | |
| | `--availability-zones` | Whether to include zone-level scores. Defaults to `true` | |
|
|
||
| **Returns:** | ||
| - `spot get`: Metadata for the Spot Placement Scores resource in the specified location, including the list of supported resource types. | ||
| - `spot generate`: A list of placement scores (High / Medium / Low) per SKU / region / availability zone, plus quota availability, indicating the likelihood that a Spot VM of the requested size can be allocated. Rate limited to 4 calls per 60 minutes per subscription. |
There was a problem hiding this comment.
Rate limited to 4 calls per 60 minutes per subscription.
If this is actually the rate limit we'll need to figure out how to test this appropriately as we can run many sets of tests in an hour and this seems like it would commonly fail on rate limiting.
| cancellationToken); | ||
|
|
||
| context.Response.Results = ResponseResult.Create( | ||
| new SpotPlacementMetadataCommandResult(metadata), |
There was a problem hiding this comment.
| new SpotPlacementMetadataCommandResult(metadata), | |
| new(metadata), |
Code style
| cancellationToken); | ||
|
|
||
| context.Response.Results = ResponseResult.Create( | ||
| new SpotPlacementScoreCommandResult(scores), |
There was a problem hiding this comment.
| new SpotPlacementScoreCommandResult(scores), | |
| new(scores), |
There was a problem hiding this comment.
Let's delete this class and just use BaseComputePlacementOptions instead. Can always add this class if we add more options to the spot placement get tool.
| var resourceId = ComputeRecommenderDiagnosticResource.CreateResourceIdentifier( | ||
| subscriptionId, new AzureLocation(location)); |
There was a problem hiding this comment.
| var resourceId = ComputeRecommenderDiagnosticResource.CreateResourceIdentifier( | |
| subscriptionId, new AzureLocation(location)); | |
| var resourceId = ComputeRecommenderDiagnosticResource.CreateResourceIdentifier(subscriptionId, new(location)); |
Code style is to use new() whenever possible.
|
|
||
| public static readonly Option<int> DesiredCount = new($"--{DesiredCountName}") | ||
| { | ||
| Description = "Number of VMs to check placement feasibility for. Must be between 1 and 1000. Defaults to 1.", |
There was a problem hiding this comment.
I don't see this --desired-count or --desired-locations being validated for their description limitations. Add that to RegisterOptions command.Validators
| --location <location> \ | ||
| --desired-locations <region1> [<region2> ...] \ |
There was a problem hiding this comment.
Based on the description of --location and --desired-location having both here seems a bit odd to me. Mind explaining why generate needs to specify an ARM region while also evaluating ARM regions
| <PackageReference Include="Azure.ResourceManager" /> | ||
| <PackageReference Include="Azure.ResourceManager.Compute" /> |
There was a problem hiding this comment.
Why did these get moved?
|
|
||
| namespace Azure.Mcp.Tools.Compute.UnitTests.PlacementScore; | ||
|
|
||
| public class SpotPlacementMetadataCommandTests |
There was a problem hiding this comment.
Unit test changes were made to use CommandUnitTestsBase<TCommand, TService> when unit testing tool commands, could you update the unit tests classes added in the PR to do so.
What does this PR do?
Adds a new MCP tool group
azmcp compute placementscore spotto the Azure Compute toolset, exposing the Azure Spot Placement Scores API for capacity-aware Spot VM placement decisions.Two subcommands:
compute placementscore spot get— Returns metadata for the Spot Placement Scores resource in a location, including supported resource types.compute placementscore spot generate— Returns placement scores (High / Medium / Low) per SKU / region / availability zone, plus quota availability, indicating the likelihood that a Spot VM of the requested size can be allocated. Rate limited to 4 calls per 60 minutes per subscription.This enables agents to evaluate Spot VM allocation likelihood across regions and availability zones to choose optimal regions and SKUs.
Documentation: https://learn.microsoft.com/azure/virtual-machines/spot-placement-scores
GitHub issue number?
N/A
Pre-merge Checklist
servers/Azure.Mcp.Server/changelog-entries/haagha-compute-spot-placement-scores.yamlcompute placementscore spottool groupservers/Azure.Mcp.Server/README.md(via existing patterns)ToolDescriptionEvaluator— all 3 test prompts rank Update README.md #1 with scores 0.40–0.55 (≥0.4 threshold met)consolidated-tools.json(via existing patterns)servers/Azure.Mcp.Server/docs/azmcp-commands.mdservers/Azure.Mcp.Server/docs/e2eTestPrompts.mdValidation
dotnet build— succeedsIsAotCompatible,EnableTrimAnalyzer,EnableAotAnalyzer) — no IL warningsInvoking Livetests
Copilot submitted PRs are not trustworthy by default. Users with
writeaccess to the repo need to validate the contents of this PR before leaving a comment with the text/azp run mcp - pullrequest - live. This will trigger the necessary livetest workflows to complete required validation.