Skip to content

Add Spot VM placement scores tool to Azure Compute#2553

Open
haagha wants to merge 5 commits intomicrosoft:mainfrom
haagha:haagha/capacity-recommendation
Open

Add Spot VM placement scores tool to Azure Compute#2553
haagha wants to merge 5 commits intomicrosoft:mainfrom
haagha:haagha/capacity-recommendation

Conversation

@haagha
Copy link
Copy Markdown
Contributor

@haagha haagha commented Apr 30, 2026

What does this PR do?

Adds a new MCP tool group azmcp compute placementscore spot to 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

  • Required for All PRs
    • Read contribution guidelines
    • PR title clearly describes the change
    • Commit history is clean with descriptive messages
    • Added comprehensive tests for new/modified functionality (18 unit tests + 2 live tests)
    • Created a changelog entry: servers/Azure.Mcp.Server/changelog-entries/haagha-compute-spot-placement-scores.yaml
  • For MCP tool changes:
    • One tool per PR: Adds only the compute placementscore spot tool group
    • Updated servers/Azure.Mcp.Server/README.md (via existing patterns)
    • Ran ToolDescriptionEvaluator — all 3 test prompts rank Update README.md #1 with scores 0.40–0.55 (≥0.4 threshold met)
    • Updated consolidated-tools.json (via existing patterns)
  • Extra steps for Azure MCP Server tool changes:
    • Updated servers/Azure.Mcp.Server/docs/azmcp-commands.md
    • Updated test prompts in servers/Azure.Mcp.Server/docs/e2eTestPrompts.md

Validation

  • dotnet build — succeeds
  • ✅ AOT analyzers (IsAotCompatible, EnableTrimAnalyzer, EnableAotAnalyzer) — no IL warnings
  • ✅ Unit tests — 14/14 PlacementScore tests passing
  • ✅ ToolDescriptionEvaluator — all prompts rank Update README.md #1, confidence 0.40+

Invoking Livetests

Copilot submitted PRs are not trustworthy by default. Users with write access 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.

Haider Agha added 5 commits March 5, 2026 08:25
…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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ComputeSetup and 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.
Comment on lines +19 to +24
ILogger<ComputePlacementService> logger)
: BaseAzureService(tenantService), IComputePlacementService
{
private readonly ISubscriptionService _subscriptionService = subscriptionService;
private readonly ILogger<ComputePlacementService> _logger = logger;

Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_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).

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +39
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
};
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@jongio jongio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +1769 to +1776
| 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` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Suggested change
| 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
new SpotPlacementMetadataCommandResult(metadata),
new(metadata),

Code style

cancellationToken);

context.Response.Results = ResponseResult.Create(
new SpotPlacementScoreCommandResult(scores),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
new SpotPlacementScoreCommandResult(scores),
new(scores),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +40 to +41
var resourceId = ComputeRecommenderDiagnosticResource.CreateResourceIdentifier(
subscriptionId, new AzureLocation(location));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this --desired-count or --desired-locations being validated for their description limitations. Add that to RegisterOptions command.Validators

Comment on lines +1757 to +1758
--location <location> \
--desired-locations <region1> [<region2> ...] \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +17 to +18
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Azure.ResourceManager.Compute" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did these get moved?


namespace Azure.Mcp.Tools.Compute.UnitTests.PlacementScore;

public class SpotPlacementMetadataCommandTests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants