Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"plugins": [
{
"name": "microsoft-finops-toolkit",
"version": "15.0-dev",
"source": "./src/templates/claude-plugin",
"version": "15.0.0",
"source": "./plugins/microsoft-finops-toolkit",
"description": "AI-powered cloud financial management for Azure. Analyze costs with KQL queries against FinOps hubs, get CFO-level reporting, and access Azure Cost Management insights.",
"category": "finops",
"homepage": "https://learn.microsoft.com/en-us/cloud-computing/finops/toolkit/finops-toolkit-overview"
Expand Down
30 changes: 30 additions & 0 deletions .github/plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "finops-toolkit",
"owner": {
"name": "Microsoft"
},
"metadata": {
"description": "Microsoft FinOps Toolkit plugins for AI-powered cloud financial management.",
"version": "15.0.0"
},
"plugins": [
{
"name": "microsoft-finops-toolkit",
Comment thread
MSBrett marked this conversation as resolved.
"version": "15.0.0",
"source": "./plugins/microsoft-finops-toolkit",
"description": "AI-powered cloud financial management for Azure. Analyze costs with KQL queries against FinOps hubs, get CFO-level reporting, and access Azure Cost Management insights.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 [AI][Claude Code] 💡 Suggestion

This description (and the matching one in .claude-plugin/marketplace.json) advertises "...and access Azure Cost Management insights", but the dedicated azure-cost-management skill that delivered those reference capabilities was removed in this PR. Consider re-wording to match what the consolidated plugin actually ships — FinOps hubs KQL analysis, CFO-level reporting, and Azure capacity evidence — so the marketplace listing isn't promising a removed surface.

"category": "finops",
"homepage": "https://learn.microsoft.com/en-us/cloud-computing/finops/toolkit/finops-toolkit-overview"
},
{
Comment thread
MSBrett marked this conversation as resolved.
"name": "microsoft-learn",
"source": {
"source": "url",
"url": "https://github.com/microsoftdocs/mcp.git"
},
"description": "Access official Microsoft documentation, API references, and code samples for Azure, .NET, Windows, and more.",
"category": "documentation",
"homepage": "https://learn.microsoft.com"
}
]
}
1 change: 1 addition & 0 deletions .plugin
17 changes: 16 additions & 1 deletion docs-mslearn/toolkit/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: FinOps toolkit changelog
description: Review the latest features and enhancements in the FinOps toolkit, including updates to FinOps hubs, Power BI reports, and more.
author: MSBrett
ms.author: brettwil
ms.date: 06/19/2026
ms.date: 06/22/2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 [AI][Claude Code] ⚠️ Should fix

Changed docs-mslearn/** files must set ms.date to today's date (MM/DD/YYYY) per repo policy — the CI date-update workflow can't push to protected branches, so it has to be set manually. This is currently 06/22/2026. Update to the date this PR merges.

ms.topic: reference
ms.service: finops
ms.subservice: finops-toolkit
Expand Down Expand Up @@ -44,6 +44,21 @@ _Released June 2026_
- Added Claude Code plugin with skills for FinOps hubs and Azure Cost Management ([#2043](https://github.com/microsoft/finops-toolkit/pull/2043)).
- Added 4 agents (CFO, FinOps practitioner, database query, hubs agent), 5 commands (`/ftk-hubs-connect`, `/ftk-hubs-healthCheck`, `/ftk-mom-report`, `/ftk-ytd-report`, `/ftk-cost-optimization`), and an output style.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🤖 [AI][Claude Code] 💡 Suggestion

This Added bullet lists 5 commands including /ftk-cost-optimization, but the Changed bullets below remove it within the same unreleased v15 — net-zero for a version that hasn't shipped. Consider collapsing to the final shipped command set (the 4 /ftk/* commands) so the changelog reflects what users actually get.

- Linked to the existing KQL query catalog in `src/queries/` from the plugin.
- **Changed**
- Consolidated Claude Code and GitHub Copilot CLI plugin assets into a shared `agent-plugin` template to keep manifests, agents, commands, and skills in sync ([#2167](https://github.com/microsoft/finops-toolkit/pull/2167)).
- Deprecated the `azure-cost-management` skill because those capabilities moved upstream to the Azure MCP server.
- Removed the `/ftk/cost-optimization` command from the shared command set.

### GitHub Copilot CLI plugin v15

- **Added**
- Added a GitHub Copilot CLI plugin with the shared FinOps Toolkit skill, 5 agents, 4 commands (`/ftk/hubs-connect`, `/ftk/hubs-healthCheck`, `/ftk/mom-report`, and `/ftk/ytd-report`), and a read-only Azure MCP server for running KQL against FinOps hubs ([#2167](https://github.com/microsoft/finops-toolkit/pull/2167)).
- Published a plugin marketplace and repo-level discovery so the plugin installs with `copilot plugin install microsoft/finops-toolkit`.
- **Changed**
- Moved plugin assets to a single shared source (`src/templates/agent-plugin`) so Claude Code and Copilot CLI use identical content.
- Deprecated the `azure-cost-management` skill because those capabilities moved upstream to the Azure MCP server.
- Removed the `/ftk/cost-optimization` command from the shared command set.
- Added an `azure-capacity-manager` agent and restructured the CFO, FinOps practitioner, and database query agents around evidence delegation across both plugins.

### [FinOps hubs](hubs/finops-hubs-overview.md) v15

Expand Down
1 change: 1 addition & 0 deletions plugins/microsoft-finops-toolkit
114 changes: 114 additions & 0 deletions src/powershell/Tests/Unit/AgentPlugins.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

BeforeAll {
$script:RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../../../..')).Path
$script:Plugin = Join-Path $script:RepoRoot 'src/templates/agent-plugin'
$script:PluginSource = './plugins/microsoft-finops-toolkit'
}

Describe 'Agent plugin manifest' {
It 'Ships a root plugin.json for GitHub Copilot CLI' {
Join-Path $script:Plugin 'plugin.json' | Should -Exist
}

It 'Ships a .claude-plugin/plugin.json for Claude' {
Join-Path $script:Plugin '.claude-plugin/plugin.json' | Should -Exist
}

It 'Uses a consistent plugin name across both manifests' {
$root = Get-Content (Join-Path $script:Plugin 'plugin.json') -Raw | ConvertFrom-Json
$claude = Get-Content (Join-Path $script:Plugin '.claude-plugin/plugin.json') -Raw | ConvertFrom-Json
$root.name | Should -Be $claude.name
}

It 'Points the agents field at a directory that exists' {
$root = Get-Content (Join-Path $script:Plugin 'plugin.json') -Raw | ConvertFrom-Json
Join-Path $script:Plugin ($root.agents -replace '^\./', '') | Should -Exist
}

It 'Loads MCP servers from .mcp.json in the plugin root' {
$root = Get-Content (Join-Path $script:Plugin 'plugin.json') -Raw | ConvertFrom-Json
$claude = Get-Content (Join-Path $script:Plugin '.claude-plugin/plugin.json') -Raw | ConvertFrom-Json

$root.mcpServers | Should -Be '.mcp.json'
$claude.mcpServers | Should -BeNullOrEmpty
$claude.agents | Should -BeNullOrEmpty
Join-Path $script:Plugin '.mcp.json' | Should -Exist
}

It 'Uses unpinned Azure MCP latest package in .mcp.json' {
$mcp = Get-Content (Join-Path $script:Plugin '.mcp.json') -Raw | ConvertFrom-Json
$args = $mcp.mcpServers.'azure-mcp-server'.args
$packageArg = $args | Where-Object { $_ -like '@azure/mcp@*' } | Select-Object -First 1

$packageArg | Should -Not -BeNullOrEmpty
$packageArg | Should -Be '@azure/mcp@latest'
}
}

Describe 'Agent plugin components' {
It 'Ships agent definitions as NAME.agent.md files' {
$agents = Get-ChildItem (Join-Path $script:Plugin 'agents') -Filter '*.agent.md'
$agents.Count | Should -BeGreaterThan 0
}

It 'Gives every agent a name and description in front matter' {
Get-ChildItem (Join-Path $script:Plugin 'agents') -Filter '*.agent.md' | ForEach-Object {
$content = Get-Content $_.FullName -Raw
$content | Should -Match '(?ms)^---\s.*^name:\s*\S.*^description:\s*\S.*^---'
}
}

It 'Ships slash commands under the ftk namespace' {
$commands = Get-ChildItem (Join-Path $script:Plugin 'commands/ftk') -Filter '*.md'
$commands.Count | Should -BeGreaterThan 0
}

It 'Ships the finops-toolkit skill' {
Join-Path $script:Plugin 'skills/finops-toolkit/SKILL.md' | Should -Exist
}

It 'Documents required kusto_query parameters in skill docs' {
foreach ($doc in @('skills/finops-toolkit/SKILL.md', 'skills/finops-toolkit/README.md'))
{
$content = Get-Content (Join-Path $script:Plugin $doc) -Raw
$content | Should -Match 'azure-mcp-server'
$content | Should -Match 'kusto_query'
$content | Should -Match '"subscription"\s*:'
}
}

It 'Ships plugin root README for marketplace onboarding' {
Join-Path $script:Plugin 'README.md' | Should -Exist
}
}

Describe 'Deprecated azure-cost-management skill' {
It 'No longer ships the azure-cost-management skill' {
Join-Path $script:Plugin 'skills/azure-cost-management' | Should -Not -Exist
}

It 'Is not referenced by any plugin manifest' {
foreach ($manifest in @('plugin.json', '.claude-plugin/plugin.json'))
{
$content = Get-Content (Join-Path $script:Plugin $manifest) -Raw
$content | Should -Not -Match 'azure-cost-management'
}
}
}

Describe 'Plugin discovery and marketplaces' {
It 'Resolves the .plugin discovery pointer to a plugin manifest' {
Join-Path $script:RepoRoot '.plugin/plugin.json' | Should -Exist
}

It 'Points every marketplace source at the plugin directory' {
foreach ($marketplace in @('.github/plugin/marketplace.json', '.claude-plugin/marketplace.json'))
{
$json = Get-Content (Join-Path $script:RepoRoot $marketplace) -Raw | ConvertFrom-Json
$entry = $json.plugins | Where-Object { $_.name -eq 'microsoft-finops-toolkit' }
$entry.source | Should -Be $script:PluginSource
}
}
}
31 changes: 31 additions & 0 deletions src/scripts/Build-AgentPlugin.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]
$DestDir
)

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path
$skillsDir = Join-Path $DestDir 'skills'

if (-not (Test-Path $skillsDir))
{
return
}

$finopsSkill = Join-Path $skillsDir 'finops-toolkit'
if (Test-Path $finopsSkill)
{
$queryDest = Join-Path $finopsSkill 'references/queries'
Remove-Item $queryDest -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item (Join-Path $repoRoot 'src/queries') -Destination $queryDest -Recurse -Force

$docsDest = Join-Path $finopsSkill 'references/docs-mslearn'
Remove-Item $docsDest -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item (Join-Path $repoRoot 'docs-mslearn') -Destination $docsDest -Recurse -Force
}

Get-ChildItem $DestDir -Force -Recurse -Filter '.DS_Store' | Remove-Item -Force
2 changes: 1 addition & 1 deletion src/scripts/Build-Toolkit.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ $templates | ForEach-Object {
# Create target directory
$destDir = "$outdir/$templateName"
Write-Verbose " Creating target directory: $destDir"
Remove-Item $destDir -Recurse -ErrorAction SilentlyContinue
Remove-Item $destDir -Recurse -Force -ErrorAction SilentlyContinue
& "$PSScriptRoot/New-Directory.ps1" $destDir

# Copy required files
Expand Down
32 changes: 21 additions & 11 deletions src/scripts/Update-Version.ps1
Comment thread
MSBrett marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ if ($update -or $Version)
$newLabel = $Label.ToLower() -replace '[^a-z]', ''
Write-Verbose "Using label '$newLabel'."
# Read directly from package.json here (rather than calling Get-Version) because $ver isn't set yet.
# Get-Version runs after this block (line 95) and reads the same file.
# Get-Version runs after this block and reads the same file.
# This intentionally replaces any prerelease counter npm just wrote — only -Major/-Minor with a label is supported.
$bumpedVer = (Get-Content (Join-Path $PSScriptRoot ../../package.json) | ConvertFrom-Json).version
$baseVer = $bumpedVer -replace '-.*$', ''
Expand Down Expand Up @@ -121,27 +121,37 @@ if ($update -or $Version)
# Update version in plugin.json files
Write-Verbose "Updating plugin.json files..."
Get-ChildItem $repoRoot -Include "plugin.json" -Recurse -Force `
| Where-Object { $_.FullName -like "*claude-plugin*" } `
| ForEach-Object {
Write-Verbose "- $($_.FullName.Replace($repoRoot + [IO.Path]::DirectorySeparatorChar, ''))"
$json = Get-Content $_ -Raw | ConvertFrom-Json
$json.version = $ver
$json | ConvertTo-Json -Depth 10 | Out-File $_ -Encoding utf8 -NoNewline
if ($json.PSObject.Properties['name'] -and $json.PSObject.Properties['version'])
{
Write-Verbose "- $($_.FullName.Replace($repoRoot + [IO.Path]::DirectorySeparatorChar, ''))"
$json.version = $ver
$json | ConvertTo-Json -Depth 10 | Out-File $_ -Encoding utf8 -NoNewline
}
}

# Update version in marketplace.json plugin entries
Write-Verbose "Updating marketplace.json files..."
Get-ChildItem $repoRoot -Include "marketplace.json" -Recurse -Force `
| Where-Object { $_.Directory.Name -eq '.claude-plugin' }`
| ForEach-Object {
Write-Verbose "- $($_.FullName.Replace($repoRoot + [IO.Path]::DirectorySeparatorChar, ''))"
$json = Get-Content $_ -Raw | ConvertFrom-Json
foreach ($plugin in $json.plugins) {
if ($plugin.PSObject.Properties['version']) {
$plugin.version = $ver
if ($json.PSObject.Properties['plugins'])
{
Write-Verbose "- $($_.FullName.Replace($repoRoot + [IO.Path]::DirectorySeparatorChar, ''))"
if ($json.PSObject.Properties['metadata'] -and $json.metadata.PSObject.Properties['version'])
{
$json.metadata.version = $ver
}
foreach ($plugin in $json.plugins)
{
if ($plugin.PSObject.Properties['version'])
{
$plugin.version = $ver
}
}
$json | ConvertTo-Json -Depth 10 | Out-File $_ -Encoding utf8 -NoNewline
}
$json | ConvertTo-Json -Depth 10 | Out-File $_ -Encoding utf8 -NoNewline
}

# Update FTK survey IDs in feedback links (e.g., surveyId/FTK0.11 -> surveyId/FTK14.0)
Expand Down
6 changes: 6 additions & 0 deletions src/templates/agent-plugin/.build.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"unversionedZip": true,
"scripts": [
"Build-AgentPlugin.ps1"
]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "finops-toolkit",
"name": "microsoft-finops-toolkit",
"version": "15.0.0",
"description": "Claude plugin for FinOps Toolkit, providing tools and integrations for FinOps practitioners.",
"author": {
Expand All @@ -11,13 +11,8 @@
"license": "MIT",
"keywords": ["finops", "cost-management", "reservations", "savings-plans", "cloud-optimization", "commitments", "credits", "macc"],
"commands": "./commands/",
"agents": ["./agents/chief-financial-officer.md", "./agents/finops-practitioner.md", "./agents/ftk-database-query.md", "./agents/ftk-hubs-agent.md"],
"skills": "./skills/",
"mcpServers": {
"azure-mcp-server": {
"command": "npx",
"args": ["-y", "@azure/mcp@latest", "server", "start", "--namespace", "kusto", "--read-only"]
}
},
"skills": [
"./skills/"
],
"outputStyles": "./output-styles/"
}
8 changes: 8 additions & 0 deletions src/templates/agent-plugin/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"azure-mcp-server": {
"command": "npx",
"args": ["-y", "@azure/mcp@latest", "server", "start", "--namespace", "kusto", "--read-only"]
}
}
}
9 changes: 9 additions & 0 deletions src/templates/agent-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# FinOps Toolkit agent plugin

This plugin is the single source artifact used by marketplace entries in this repo.

For setup and usage:

- Skill overview and task routing: `./skills/finops-toolkit/SKILL.md`
- Querying and workflows: `./skills/finops-toolkit/README.md`
- MCP server configuration: `./.mcp.json`
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
name: azure-capacity-manager
description: "Use this agent when the user needs Azure capacity evidence for FinOps work: quota analysis, capacity reservation groups, SKU availability, region or zone access, AKS capacity readiness, non-compute quotas, capacity planning, or coordination between capacity guarantees and pricing commitments."
---

You are an Azure capacity evidence specialist for FinOps workflows. You map Azure quota, region, SKU, zone, capacity reservation, AKS, and non-compute limit evidence into the canonical FinOps Framework. You do not present Azure capacity management or azcapman as a separate operating framework.

## FinOps capability mapping

Use these canonical FinOps Framework capabilities when framing capacity findings:

- **Planning & Estimating**: Size planned workloads, scale units, deployment stamps, and upcoming demand before quota, region, SKU, or reserved-capacity requests.
- **Forecasting**: Project when demand will exceed quota, access, SKU, zone, or reserved-capacity headroom.
- **Architecting & Workload Placement**: Evaluate regions, zones, SKUs, deployment stamps, quota pools, and placement constraints.
- **Usage Optimization**: Identify overallocated, underused, or inefficient quota, capacity reservation, and workload patterns.
- **Rate Optimization**: Coordinate capacity guarantees with Azure Reservations and savings plans when cost evidence supports a pricing commitment.
- **Governance, Policy & Risk**: Surface capacity, quota, region, or zone risks with owners, thresholds, exception status, and escalation paths.
- **Automation, Tools & Services**: Recommend alerts, CI/CD gates, scripts, and operating checks that expose capacity risk before deployment or scale events.

## Hard boundaries

- Capacity reservations guarantee compute supply. Azure Reservations and savings plans reduce price. They are coordinated, but they are not substitutes.
- Quota is an entitlement limit, not proof that physical capacity is available.
- Region access, quota increases, and zonal enablement are separate controls with separate approval paths.
- Logical availability-zone labels are subscription-specific. Verify physical zone mapping before cross-subscription CRG or zonal architecture decisions.
- Do not query FinOps Hub Kusto data directly. Ask `ftk-database-query` for cost, commitment, savings, recommendation, transaction, and forecast evidence.
- Do not make finance approval decisions. Consult `chief-financial-officer` through the FinOps practitioner when commitment, budget, or executive tradeoff decisions are material.

## Evidence you own

- VM family quota usage, quota headroom, quota groups, and quota transfers.
- Capacity reservation groups, CRG sharing, overallocation, utilization, and region or zone alignment.
- SKU availability, offer restrictions, region access, and zonal enablement.
- AKS node pool capacity readiness and CRG association constraints.
- Storage, networking, database, and other non-compute service quota risks.
- Azure implementation guidance from Microsoft Learn and repo-provided references.

## Collaboration model

- Work with `finops-practitioner` as the operating-rhythm owner.
- Ask `ftk-database-query` for Kusto-backed cost, commitment, recommendation, savings, and forecast evidence.
- Consult `chief-financial-officer` only through executive or financial-decision framing, not for raw telemetry collection.
- Coordinate with `ftk-hubs-agent` when capacity evidence affects FinOps Hubs platform deployment or Azure Data Explorer SKU readiness.

## Output expectations

For capacity, quota, SKU, CRG, region, zone, AKS, or PaaS limit reports, include:

1. **Summary**: Capacity posture, top blocker, exact scope, and time period.
2. **FinOps capability status**: Findings mapped to the canonical capabilities above.
3. **Risk register**: Subscription, region, service or SKU, usage, limit, utilization, headroom, source, status, and owner or action.
4. **Capacity and workload actions**: Quota increase, quota transfer, region access, zonal enablement, SKU substitution, CRG create/resize/share, alert, or policy/gate action.
5. **Confidence and caveats**: Evidence freshness, API gaps, estimated limits, zone mapping gaps, and missing owner metadata.
Loading
Loading