Skip to content

Add CostManagement query tool#2603

Open
francesco1501 wants to merge 2 commits into
microsoft:mainfrom
francesco1501:feature/add-costmanagement-query
Open

Add CostManagement query tool#2603
francesco1501 wants to merge 2 commits into
microsoft:mainfrom
francesco1501:feature/add-costmanagement-query

Conversation

@francesco1501
Copy link
Copy Markdown

Closes #2602
References #1420, #447

Summary

Adds a new Azure.Mcp.Tools.CostManagement toolset with the first command azmcp costmanagement query run, exposing the Azure Cost Management Query/Usage API to MCP clients. Read-only, subscription + optional resource-group scope. Backed by the GA SDK Azure.ResourceManager.CostManagement 1.0.3.

What's new

Area Details
Tool azmcp costmanagement query run — actual costs for a subscription (optionally narrowed to a resource group).
Timeframes MonthToDate, BillingMonthToDate, TheCurrentMonth, TheLastBillingMonth, WeekToDate, Custom (with --from/--to). TheLastMonth intentionally omitted — only supported at billing-account scope.
Granularity None (default, single total), Daily, Monthly.
Grouping Optional single dimension via --group-by (ServiceName, ResourceGroupName, ResourceLocation, ResourceId, MeterCategory, MeterSubCategory, ChargeType, BillingPeriod). Custom/tag dimensions also accepted.
Tool ID f7c4b3a8-9e62-4d18-bc41-2a5d8e6f1b09 (verified unique repo-wide)
Auth Caller needs Cost Management Reader or Reader role on the scope.
azmcp costmanagement query run --subscription <sub> \
                               [--resource-group <rg>] \
                               [--timeframe <MonthToDate|BillingMonthToDate|TheCurrentMonth|TheLastBillingMonth|WeekToDate|Custom>] \
                               [--from <YYYY-MM-DD>] [--to <YYYY-MM-DD>] \
                               [--granularity <None|Daily|Monthly>] \
                               [--group-by <ServiceName|ResourceGroupName|...>]

Implementation notes

  • Pattern: mirrors Azure.Mcp.Tools.Marketplace (newest reference) — BaseAzureService + SubscriptionCommand<TOptions> + AOT-compatible JsonSerializerContext. Tests extend CommandUnitTestsBase<TCommand, TService>.
  • Response shape: the raw API returns rows[][] (array of arrays). The service normalizes columns (Cost/PreTaxCost/CostUSD are all handled — non-obvious EA/MCA/PAYG variance), parses UsageDate (YYYYMMDD/YYYYMM ints) into ISO-8601, and returns typed CostQueryRow records — much friendlier for LLM consumption than the raw shape.
  • Validation: Custom timeframe requires both --from and --to; --from must be ≤ --to. Validated at command level via command.Validators.
  • Errors: GetErrorMessage covers 400/403/404/429 with actionable hints; GetStatusCode override maps RequestFailedException.Status directly (verified: invalid --group-by returns 400, not 500).
  • Helpers safety: ReadDecimal/ReadString throw InvalidOperationException on parse failure (no silent under-reporting for billing data); JsonValueKind.Null cleanly returns 0/null.

Tests

  • Unit tests: 30 (10 command + 20 service helpers). Service-layer helpers (ReadDecimal, ReadString, FormatUsageDate, ResolveColumnIndex, KnownDimensions) tested directly via internal static exposure.
  • Live tests: 3 cases (Should_query_subscription_costs_month_to_date, Should_query_subscription_costs_grouped_by_service, Should_query_custom_timeframe_with_daily_granularity). Recorded against an MCA subscription; recordings validated in Playback mode (3/3 passed in 5.8s with zero Azure calls). Recording files not included in this PR for organizational privacy (real cost figures and tenant identifiers); happy to either: add custom sanitizers + re-record, or let the maintainer record against an internal subscription at merge time.
  • MapResult coverage: indirect via live tests + helper unit tests. QueryResult has internal-only ctor in SDK 1.0.3 and ArmCostManagementModelFactory does not expose a builder for it (verified against the SDK source); reflection-based test would be fragile across SDK bumps.

Pre-merge checklist

  • One tool per PR — only costmanagement query run
  • git rebase against upstream/main HEAD (3c5fd84818d) — diff is purely additive: 10 wiring files +95/-0, 24 new files under tools/Azure.Mcp.Tools.CostManagement/
  • dotnet build (src + server + LiveTests) — 0 warnings, 0 errors
  • dotnet test — 30/30 passed
  • dotnet format --verify-no-changes — clean
  • ./eng/scripts/Update-AzCommandsMetadata.ps1 — ran, only adds the new tool's entry
  • ./eng/scripts/Analyze-AOT-Compact.ps1 — 0 trim/AOT warnings, 0 affected DLLs
  • ./eng/common/spelling/Invoke-Cspell.ps1 — 0 issues across 24 files (added costmanagement to .vscode/cspell.json)
  • CHANGELOG entry — servers/Azure.Mcp.Server/changelog-entries/francesco1501-add-costmanagement-query.yml with section Features Added
  • CODEOWNERS — tools-CostManagement block added
  • LiveTests project + assets.json (Tag empty for maintainer push) + test-resources.bicep (empty per Marketplace convention)
  • ToolDescriptionEvaluator ≥0.4 + top-3 — not run locally (requires Azure OpenAI text-embedding-3-large deployment unavailable to external contributors); relying on PR Validation CI
  • Live test recordings pushed to Azure/azure-sdk-assets — requires maintainer write access; assets.json Tag left empty for test-proxy push at merge time

Files changed

10 modified (wiring), 25 new (toolset + tests + changelog + bicep + assets):

  • Directory.Packages.props — adds Azure.ResourceManager.CostManagement 1.0.3
  • Microsoft.Mcp.slnx, servers/Azure.Mcp.Server/Azure.Mcp.Server.slnx — register src + UnitTests + LiveTests projects
  • servers/Azure.Mcp.Server/src/Program.cs — register CostManagementSetup() in alphabetical position
  • servers/Azure.Mcp.Server/src/Resources/consolidated-tools.json — add query_azure_costs mapping
  • servers/Azure.Mcp.Server/docs/azmcp-commands.md — sync via Update-AzCommandsMetadata.ps1
  • servers/Azure.Mcp.Server/docs/e2eTestPrompts.md — 10 test prompts
  • servers/Azure.Mcp.Server/README.md — usage example block + entry in 43+ services list
  • .github/CODEOWNERStools-CostManagement block
  • .vscode/cspell.jsoncostmanagement
  • tools/Azure.Mcp.Tools.CostManagement/ — full new toolset (src + UnitTests + LiveTests + bicep + post-deploy)

Setup notes for whoever records the live tests

Proven working procedure (in case helpful):

  1. .testsettings.json next to the LiveTests project with TestMode: "Record", IsServicePrincipal: true, and an EnvironmentVariables block including AZURE_TOKEN_CREDENTIALS=EnvironmentCredential — without this the harness prompts for browser login even with valid SP env vars
  2. Subscription must be MCA / PAYG, or an EA enrollment with AO View Charges enabled — pure EA tenants without that flag return AccountCostDisabled regardless of RBAC
  3. After recording, copy .assets/<hash>/.../SessionRecords/*.json files to commit, run test-proxy push to populate the assets.json Tag

Adds a new Azure.Mcp.Tools.CostManagement toolset with the first command
'azmcp costmanagement query run' that retrieves actual Azure costs and
usage from the Azure Cost Management Query/Usage API for a subscription
or resource group.

Features:
- Predefined timeframes (MonthToDate, BillingMonthToDate, TheCurrentMonth,
  TheLastBillingMonth, WeekToDate) and Custom range with --from/--to
- Optional Daily/Monthly granularity
- Grouping by a single Azure dimension (ServiceName, ResourceGroupName,
  ResourceLocation, ResourceId, MeterCategory, MeterSubCategory,
  ChargeType, BillingPeriod)
- Backed by Azure.ResourceManager.CostManagement 1.0.3 (GA)

Includes:
- 30 unit tests (10 command + 20 service helpers)
- LiveTests project scaffolding (assets.json with empty Tag for
  maintainer to populate at merge)
- Full CONTRIBUTING.md compliance: AOT-compatible, internal record,
  GetStatusCode override, JsonSerializerContext, sealed setup, etc.

Closes the gap tracked in microsoft#1420 (FinOps recommenders) and supersedes
the abandoned PR microsoft#447 (CostManagement Tool File Structure).
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Thank you for your contribution @francesco1501! We will review the pull request and get back to you soon.

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

This PR adds a new Azure.Mcp.Tools.CostManagement toolset to Azure MCP Server, introducing the azmcp costmanagement query run command to query actual Azure costs/usage via the Azure Cost Management Query/Usage API at subscription scope (optionally narrowed to a resource group).

Changes:

  • Added a new Cost Management area (costmanagement) with query run command, options model, service implementation, and AOT-friendly System.Text.Json source generation context.
  • Added unit tests plus recorded live tests scaffolding for the new command.
  • Wired the tool into the server (area registration, consolidated tool mapping) and updated docs/metadata (README, command list, e2e prompts, changelog, CODEOWNERS, package version).

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.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/Azure.Mcp.Tools.CostManagement/tests/test-resources.bicep Adds empty bicep template required by live test harness.
tools/Azure.Mcp.Tools.CostManagement/tests/test-resources-post.ps1 Adds standard post-deploy script to generate live test settings.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.UnitTests/Services/CostManagementServiceTests.cs Unit tests for service helper parsing/formatting utilities.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.UnitTests/Query/QueryRunCommandTests.cs Unit tests for command wiring, validation, and error handling.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.UnitTests/Azure.Mcp.Tools.CostManagement.UnitTests.csproj Adds unit test project for CostManagement tool.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.LiveTests/QueryRunCommandTests.cs Adds recorded live tests covering core query scenarios.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.LiveTests/Azure.Mcp.Tools.CostManagement.LiveTests.csproj Adds live test project for CostManagement tool.
tools/Azure.Mcp.Tools.CostManagement/tests/Azure.Mcp.Tools.CostManagement.LiveTests/assets.json Adds assets.json required for recorded test assets workflow.
tools/Azure.Mcp.Tools.CostManagement/src/Services/ICostManagementService.cs Introduces service contract for cost query execution.
tools/Azure.Mcp.Tools.CostManagement/src/Services/CostManagementService.cs Implements Cost Management query execution and response normalization.
tools/Azure.Mcp.Tools.CostManagement/src/Options/Query/QueryRunOptions.cs Adds options model for query run.
tools/Azure.Mcp.Tools.CostManagement/src/Options/CostManagementOptionDefinitions.cs Defines CLI options for timeframe/date range/granularity/grouping.
tools/Azure.Mcp.Tools.CostManagement/src/Options/BaseCostManagementOptions.cs Introduces common options base for CostManagement commands.
tools/Azure.Mcp.Tools.CostManagement/src/Models/QueryTimeframe.cs Adds supported timeframe enum (incl. Custom).
tools/Azure.Mcp.Tools.CostManagement/src/Models/QueryGranularity.cs Adds granularity enum (None/Daily/Monthly).
tools/Azure.Mcp.Tools.CostManagement/src/Models/CostQueryRow.cs Defines typed row model returned to clients.
tools/Azure.Mcp.Tools.CostManagement/src/Models/CostQueryResult.cs Defines top-level result model returned to clients.
tools/Azure.Mcp.Tools.CostManagement/src/GlobalUsings.cs Adds global using for System.CommandLine.
tools/Azure.Mcp.Tools.CostManagement/src/CostManagementSetup.cs Registers DI + command groups for the new area.
tools/Azure.Mcp.Tools.CostManagement/src/Commands/Query/QueryRunCommand.cs Implements costmanagement query run command and validation/error mapping.
tools/Azure.Mcp.Tools.CostManagement/src/Commands/CostManagementJsonContext.cs Adds source-generated JSON serialization context for AOT.
tools/Azure.Mcp.Tools.CostManagement/src/Commands/BaseCostManagementCommand.cs Adds shared command base for CostManagement (subscription + resource group option).
tools/Azure.Mcp.Tools.CostManagement/src/Azure.Mcp.Tools.CostManagement.csproj Adds new tool project and dependencies including CostManagement SDK.
tools/Azure.Mcp.Tools.CostManagement/src/AssemblyInfo.cs Enables internals visibility for unit/live tests.
servers/Azure.Mcp.Server/src/Resources/consolidated-tools.json Adds consolidated tool entry mapping to costmanagement_query_run.
servers/Azure.Mcp.Server/src/Program.cs Registers CostManagementSetup in server area registration.
servers/Azure.Mcp.Server/README.md Documents Cost Management prompts and adds it to services list.
servers/Azure.Mcp.Server/docs/e2eTestPrompts.md Adds E2E prompts for costmanagement_query_run.
servers/Azure.Mcp.Server/docs/azmcp-commands.md Adds CLI documentation block for the new command.
servers/Azure.Mcp.Server/changelog-entries/francesco1501-add-costmanagement-query.yml Adds changelog entry announcing the new tool.
servers/Azure.Mcp.Server/Azure.Mcp.Server.slnx Adds new tool + test projects to server solution.
Microsoft.Mcp.slnx Adds new tool + test projects to repo solution.
Directory.Packages.props Adds Azure.ResourceManager.CostManagement package version.
.vscode/cspell.json Adds costmanagement to spelling dictionary.
.github/CODEOWNERS Adds CODEOWNERS entry/label for CostManagement tool.

Comment on lines +54 to +55
Assert.True(rows.GetArrayLength() > 0,
"Expected at least one cost row when grouping by ServiceName MTD against a subscription with known spend.");
Comment on lines +63 to +70
new()
{
{ "subscription", Settings.SubscriptionId },
{ "timeframe", "Custom" },
{ "from", "2026-04-01" },
{ "to", "2026-04-07" },
{ "granularity", "Daily" }
});
Azure.ResourceManager.CostManagement 1.0.3 transitively requires
Azure.Core >= 1.53.0. Bumping Azure.Core to 1.53 introduces a
CredentialUnavailableException type collision with Azure.Identity 1.17.1
in core/Azure.Mcp.Core/tests/.../CustomChainedCredentialTests.cs (CS0433).

Downgrade to 1.0.2 (which only requires Azure.Core >= 1.44.1) to keep
the existing Azure.Core 1.50 pin. Trade-off: 1.0.2 lacks two enum
values that we removed from our QueryTimeframe / QueryGranularity:

- QueryTimeframe.TheCurrentMonth (use MonthToDate instead)
- QueryGranularity.Monthly       (use Daily for breakdown,
                                  None for aggregated total)

Updated docs (azmcp-commands.md, consolidated-tools.json, changelog
entry) and option descriptions accordingly.
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.

Clean implementation that follows the repo's toolset patterns well. The service layer handles response normalization thoughtfully, covering variant column names (Cost/PreTaxCost/CostUSD) across billing account types and date format parsing for both daily and monthly granularities. The Custom timeframe validation via command.Validators is the right approach, and the error handling maps SDK exceptions to actionable user messages.

A few items for maintainers:

CI: The mcp - pullrequest pipeline shows 25 errors, but the author reports clean local builds (0 warnings, 30/30 tests). Worth checking the Azure DevOps logs since this could be a pipeline config issue for fork/community PRs.

SDK version pinning: Pinning to Azure.ResourceManager.CostManagement 1.0.2 to avoid the Azure.Core version conflict is a reasonable trade-off. The removed enum values (TheCurrentMonth, Monthly) are correctly excluded from the option definitions and documented in the PR body.

Live tests: The bot's two comments about hardcoded dates and the rows > 0 assertion are worth addressing before merge.

Comment thread .github/CODEOWNERS
/tools/Azure.Mcp.Tools.CostManagement/ @francesco1501 @microsoft/azure-mcp

# ServiceLabel: %tools-CostManagement
# ServiceOwners: @francesco1501
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 ServiceOwners line lists only the external contributor. Other toolsets in this repo have at least one Microsoft team member as ServiceOwner/CODEOWNER. Consider adding a team member alongside so code ownership doesn't depend entirely on an external contributor's availability.

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

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

Add command: azmcp costmanagement query run

3 participants