Skip to content

Commit 5de8f21

Browse files
authored
Fix tool eval scripts and update persisted tool hierarchy json data (#1035)
* Fix tool eval scripts and update persisted tool hierarchy json data * Update namespace prompts data * Update tool evaluation results * Fix ToolListCommandTest
1 parent e9cb97a commit 5de8f21

File tree

11 files changed

+2300
-2175
lines changed

11 files changed

+2300
-2175
lines changed

core/Azure.Mcp.Core/src/Areas/Tools/Commands/ToolsListCommand.cs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.Runtime.InteropServices;
45
using Azure.Mcp.Core.Areas.Tools.Options;
56
using Azure.Mcp.Core.Commands;
67
using Azure.Mcp.Core.Models.Option;
8+
using Microsoft.AspNetCore.Mvc.ModelBinding;
79
using Microsoft.Extensions.Logging;
810

911
namespace Azure.Mcp.Core.Areas.Tools.Commands;
@@ -77,23 +79,15 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
7779
.ToList();
7880

7981
// Add the commands to be surfaced directly to the list.
82+
// For commands in the surfaced list, each command is exposed as a separate tool in the namespace mode.
8083
foreach (var name in surfaced)
8184
{
8285
var subgroup = rootGroup.SubGroup.FirstOrDefault(g => string.Equals(g.Name, name, StringComparison.OrdinalIgnoreCase));
8386
if (subgroup is not null)
8487
{
85-
var commands = CommandFactory.GetVisibleCommands(subgroup.Commands).Select(kvp =>
86-
{
87-
var command = kvp.Value.GetCommand();
88-
return new CommandInfo
89-
{
90-
Name = command.Name,
91-
Description = command.Description ?? string.Empty,
92-
Command = $"{subgroup.Name} {command.Name}"
93-
// Omit Options and Subcommands for surfaced commands as well.
94-
};
95-
});
96-
namespaceCommands.AddRange(commands);
88+
List<CommandInfo> foundCommands = [];
89+
searchCommandInCommandGroup("", subgroup, foundCommands);
90+
namespaceCommands.AddRange(foundCommands);
9791
}
9892
}
9993

@@ -138,4 +132,24 @@ private static CommandInfo CreateCommand(string tokenizedName, IBaseCommand comm
138132
Metadata = command.Metadata
139133
};
140134
}
135+
136+
private void searchCommandInCommandGroup(string commandPrefix, CommandGroup searchedGroup, List<CommandInfo> foundCommands)
137+
{
138+
var commands = CommandFactory.GetVisibleCommands(searchedGroup.Commands).Select(kvp =>
139+
{
140+
var command = kvp.Value.GetCommand();
141+
return new CommandInfo
142+
{
143+
Name = $"{commandPrefix.Replace(" ", "_")}{searchedGroup.Name}_{command.Name}",
144+
Description = command.Description ?? string.Empty,
145+
Command = $"{(!string.IsNullOrEmpty(commandPrefix) ? commandPrefix : "")}{searchedGroup.Name} {command.Name}"
146+
// Omit Options and Subcommands for surfaced commands as well.
147+
};
148+
});
149+
foundCommands.AddRange(commands);
150+
foreach (CommandGroup nextLevelSubGroup in searchedGroup.SubGroup)
151+
{
152+
searchCommandInCommandGroup($"{commandPrefix}{searchedGroup.Name} ", nextLevelSubGroup, foundCommands);
153+
}
154+
}
141155
}

core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ public async Task ExecuteAsync_WithNamespaceSwitch_ReturnsNamespacesOnly()
353353
else
354354
{
355355
// Surfaced extension command: Command is "{namespace} {commandName}", Name is just "{commandName}"
356-
Assert.EndsWith(ns.Name, ns.Command);
356+
// When Azure MCP presents the commands as tools, the spaces in the commands are replaced by underscore
357+
Assert.EndsWith(ns.Name, ns.Command.Replace(" ", "_"));
357358
}
358359

359360
Assert.Equal(ns.Name, ns.Name.Trim());

eng/tools/ToolDescriptionEvaluator/Generate-GroupedPromptsJson.ps1

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ Generates prompts JSON files for consolidated tools, namespace tools, or both.
55
.DESCRIPTION
66
Modes:
77
Consolidated - Produces consolidated-prompts.json mapping each consolidated tool name to aggregated prompts.
8-
Namespace - Produces namespace-prompts.json mapping each namespace to aggregated prompts from all descendant subcommands. Keys are prefixed with 'azmcp_' for consistency with per-command prompt keys.
8+
Namespace - Produces namespace-prompts.json mapping each namespace to aggregated prompts from all descendant subcommands.
99
Both - Produces both outputs in a single run.
1010
1111
Reads:
1212
- consolidated-tools.json (contains consolidated_tools array; each tool has an mappedToolList list)
1313
- namespace-tools.json (contains top-level namespaces with recursive subcommands)
1414
- tools.json (optional for future enrichment / validation)
15-
- prompts.json (maps individual command keys to prompt examples; e.g. command "azmcp acr registry list" => key "azmcp_acr_registry_list")
15+
- prompts.json (maps individual command keys to prompt examples; e.g. command "acr registry list" => key "acr_registry_list")
1616
1717
For every entity (consolidated tool name OR namespace name) the script:
1818
1. Resolves its underlying command strings
@@ -56,7 +56,7 @@ Idempotent. Safe to re-run. Designed to be executed from repo root or script dir
5656
param(
5757
[Parameter(Mandatory)][ValidateSet('Consolidated','Namespace','Both')][string]$Mode,
5858
[string]$ConsolidatedToolsPath = "../../../core/Azure.Mcp.Core/src/Areas/Server/Resources/consolidated-tools.json",
59-
[string]$NamespaceToolsPath = "./namespace-tools.json",
59+
[string]$NamespaceToolsPath = "./namespace-tools.json", # This file contains the output of the `azmcp tools list --namespaces` command.
6060
[string]$PromptsPath = "./prompts.json",
6161
[string]$ToolsPath = "./tools.json",
6262
[string]$OutputPath,
@@ -126,12 +126,12 @@ function Resolve-PromptsForCommands {
126126

127127
<#
128128
NOTE: Namespace tools JSON is now a FLAT list where each element is either:
129-
1. A top-level namespace entry: { name, command: "azmcp <ns>" }
130-
2. A surfaced leaf command entry: { name, command: "azmcp extension <leaf>" }
129+
1. A top-level namespace entry: { name, command: "<ns>" }
130+
2. A surfaced leaf command entry: { name, command: "extension <leaf>" }
131131
132132
Previously the structure contained recursive subcommands; the old recursive walker is retained
133133
only for backwards compatibility scenarios (not currently invoked). The new logic derives the
134-
leaf command set for a namespace by scanning all prompt keys with the prefix 'azmcp_<ns>_'.
134+
leaf command set for a namespace by scanning all prompt keys with the prefix '<ns>_'.
135135
Surfaced leaf commands simply aggregate their own prompts.
136136
#>
137137
function Get-NamespaceCommandStrings {
@@ -144,11 +144,11 @@ function Get-NamespaceCommandStrings {
144144
if ($null -eq $Node -or -not $Node.command) { return $acc }
145145
$cmd = ($Node.command -replace '\s+', ' ').Trim()
146146

147-
# Pattern: azmcp <namespace>
148-
if ($cmd -match '^(azmcp)\s+([^\s]+)$') {
149-
$ns = $Matches[2]
150-
# Collect all prompt keys beginning with azmcp_<ns>_ (leaf commands)
151-
$prefix = "azmcp_${ns}_"
147+
# Pattern: <namespace>
148+
if ($cmd -match '^([^\s]+)$') {
149+
$ns = $Matches[1]
150+
# Collect all prompt keys beginning with <ns>_ (leaf commands)
151+
$prefix = "${ns}_"
152152
$matchingLeafKeys = $AllPromptKeys | Where-Object { $_.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase) }
153153
foreach ($k in $matchingLeafKeys) {
154154
# Convert prompt key back to command string: underscores -> spaces
@@ -157,7 +157,7 @@ function Get-NamespaceCommandStrings {
157157
return ($acc | Sort-Object -Unique)
158158
}
159159

160-
# Surfaced leaf (e.g., azmcp extension azqr) – aggregate only itself
160+
# Surfaced leaf (e.g., extension azqr) – aggregate only itself
161161
$acc += $cmd
162162
return $acc
163163
}
@@ -233,12 +233,11 @@ function Invoke-NamespaceGeneration {
233233

234234
$commandStrings = @(Get-NamespaceCommandStrings -Node $ns -AllPromptKeys $AllPromptKeys)
235235

236-
# Determine aggregation key: namespace entries get 'azmcp_<ns>', surfaced leaf commands keep their converted key
237-
$isNamespace = $ns.command -match '^(azmcp)\s+([^\s]+)$'
236+
# Determine aggregation key: namespace entries get '<ns>', surfaced leaf commands keep their converted key
237+
238+
$isNamespace = $ns.command -match '^([^\s]+)$'
238239
if ($isNamespace) {
239-
$nsName = $Matches[2]
240-
switch ($nsName) { 'azqr' { $nsName = 'extension_azqr'; break } }
241-
if (-not $nsName.StartsWith('azmcp_')) { $nsName = 'azmcp_' + $nsName }
240+
$nsName = $Matches[1]
242241
$resolved = Resolve-PromptsForCommands -CommandStrings $commandStrings -PromptsJson $PromptsJson -AllPromptKeys $AllPromptKeys -Warnings ([ref]$warnings) -VerboseWarnings:$VerboseWarnings
243242
$outputMap[$nsName] = @($resolved)
244243
}

eng/tools/ToolDescriptionEvaluator/Generate-PromptsJsonFromMarkdown.ps1

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ with the same base file name as the source markdown file (e.g. prompts.md -> pro
2121
Generates a prompts.json file based on the specified markdown in the current directory.
2222
2323
.EXAMPLE
24-
./Generate-PromptsJsonFromMarkdown.ps1 -PromptsSource ./prompts.md -OutputPath ./eng/tools/ToolDescriptionEvaluator/myprompts.json
24+
./Generate-PromptsJsonFromMarkdown.ps1 -PromptsSource ./prompts.md -OutputPath ./myprompts.json
2525
Generates a JSON file with the specified name and path. If no filename is provided, the same name as the source markdown
2626
is used with a .json extension.
2727
@@ -87,7 +87,6 @@ function Get-ToolPrompts([string]$Path) {
8787
if ($parts.Count -lt 2) { return }
8888
$tool = $parts[0]
8989
$prompt = $parts[1]
90-
if (-not $tool.StartsWith('azmcp_')) { return }
9190
if (-not $prompt) { return }
9291
if (-not $result.ContainsKey($tool)) { $result[$tool] = @() }
9392
$result[$tool] += (Convert-Special-Characters $prompt)

eng/tools/ToolDescriptionEvaluator/consolidated-prompts.json

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323
"What is the status of function app <function_app_name>?"
2424
],
2525
"add_azure_app_service_database": [
26-
"Add a CosmosDB database to app service <app_name>",
27-
"Add a database connection to my app service <app_name> in resource group <resource_group>",
28-
"Add a MySQL database to app service <app_name>",
29-
"Add a PostgreSQL database to app service <app_name>",
30-
"Add database <database_name> on server <database_server> to app service <app_name>",
31-
"Add database <database_name> with retry policy to app service <app_name>",
32-
"Configure a SQL Server database for app service <app_name>",
33-
"Configure tenant <tenant> for database <database_name> in app service <app_name>",
34-
"Set connection string for database <database_name> in app service <app_name>"
26+
"Add database connection <connection_string> for database <database_name> on server <database_server> to app service <app_name> in resource group <resource_group>",
27+
"Add database connection <connection_string> to my app service <app_name> for database <database_name> in resource group <resource_group>",
28+
"Add database connection string for <database_name> to app service <app_name> using connection string <connection_string> in resource group <resource_group>",
29+
"Add MySQL database <database_name> to app service <app_name> using connection <connection_string> in resource group <resource_group>",
30+
"Add PostgreSQL database <database_name> to app service <app_name> using connection <connection_string> in resource group <resource_group>",
31+
"Configure database <database_name> for app service <app_name> with the connection string <connection_string> in resource group <resource_group>",
32+
"Configure SQL Server database <database_name> for app service <app_name> with connection string <connection_string> in resource group <resource_group>",
33+
"Connect CosmosDB database <database_name> using connection string <connection_string> to app service <app_name> in resource group <resource_group>",
34+
"Connect database <database_name> to my app service <app_name> using connection string <connection_string> in resource group <resource_group>",
35+
"Set up database <database_name> for app service <app_name> with connection string <connection_string> under resource group <resource_group>"
3536
],
3637
"get_azure_databases_details": [
3738
"Display the properties of SQL server <server_name>",
@@ -197,12 +198,12 @@
197198
"Scan my Azure subscription for compliance recommendations"
198199
],
199200
"generate_azure_cli_commands": [
200-
"Get Azure CLI command to create a Storage account with name <storage_account_name>",
201-
"Show me how to use Azure CLI to list all virtual machines in my subscription",
201+
"Create a Storage account with name <storage_account_name> using Azure CLI",
202+
"List all virtual machines in my subscription using Azure CLI",
202203
"Show me the details of the storage account <account_name> with Azure CLI commands"
203204
],
204205
"install_azure_cli_extensions": [
205-
"Get Azure CLI installation instructions",
206+
"<Ask the MCP host to uninstall az cli on your machine and run test prompts for extension_cli_generate>",
206207
"How to install azd",
207208
"What is Azure Functions Core tools and how to install it"
208209
],
@@ -277,6 +278,7 @@
277278
"Upload certificate file <file_path> to key vault <key_vault_account_name>"
278279
],
279280
"get_azure_best_practices": [
281+
"configure azure mcp in coding agent for my repo",
280282
"Fetch the Azure Terraform best practices",
281283
"Get the latest Azure best practices",
282284
"Get the latest Azure code generation best practices",
@@ -291,7 +293,7 @@
291293
],
292294
"design_azure_architecture": [
293295
"Generate the azure architecture diagram for this application",
294-
"Help me create a cloud service that will serve as ATM for users",
296+
"Help me design an Azure cloud service that will serve as an ATM for users",
295297
"How can I design a cloud service in Azure that will store and present videos for users?",
296298
"I want to design a cloud app for ordering groceries",
297299
"Please help me design an architecture for a large-scale file upload, storage, and retrieval service"
@@ -315,11 +317,11 @@
315317
"Get the details of knowledge base <agent-name> in the Azure AI Search service <service-name>",
316318
"Get the details of knowledge source <source-name> in the Azure AI Search service <service-name>",
317319
"Get the schema configuration for knowledge index <index-name>",
318-
"List all agents in my AI Foundry project",
320+
"List all agents in my Azure AI Foundry resource",
319321
"List all AI Foundry model deployments",
320322
"List all AI Foundry models",
321323
"List all AI Foundry resources in my subscription",
322-
"List all available OpenAI models in my Azure resource",
324+
"List all available OpenAI models in my Azure AI Foundry resource",
323325
"List all Cognitive Search services in my subscription",
324326
"List all indexes in the Cognitive Search service <service-name>",
325327
"List all knowledge bases in the Azure AI Search service <service-name>",
@@ -331,7 +333,7 @@
331333
"Show me all AI Foundry model deployments",
332334
"Show me my Cognitive Search services",
333335
"Show me the AI Foundry resources in resource group <resource_group_name>",
334-
"Show me the available agents in my AI Foundry project",
336+
"Show me the available agents in my Azure AI Foundry resource",
335337
"Show me the available AI Foundry models",
336338
"Show me the Cognitive Search services in my subscription",
337339
"Show me the details of the index <index-name> in Cognitive Search service <service-name>",
@@ -343,8 +345,8 @@
343345
"Show me the knowledge source <source-name> in search service <service-name>",
344346
"Show me the knowledge sources in the Azure AI Search service <service-name>",
345347
"Show me the knowledge sources in the search service <service-name>",
346-
"Show me the OpenAI model deployments",
347-
"Show me the schema for knowledge index <index-name> in my AI Foundry project"
348+
"Show me the OpenAI model deployments in my Azure AI Foundry resource",
349+
"Show me the schema for knowledge index <index-name> in my Azure AI Foundry resource"
348350
],
349351
"retrieve_azure_ai_knowledge_base_content": [
350352
"Ask knowledge base <agent-name> in search service <service-name> to retrieve information about <query>",
@@ -356,16 +358,16 @@
356358
"What does knowledge base <agent-name> in search service <service-name> know about <query>"
357359
],
358360
"use_azure_openai_models": [
359-
"Create a chat completion with the message \"Hello, how are you today?\"",
360-
"Create a completion with the prompt \"What is Azure?\"",
361-
"Create vector embeddings for my text using Azure OpenAI",
362-
"Generate embeddings for the text \"Azure OpenAI Service\""
361+
"Create a chat completion with the message \"Hello, how are you today?\" using my Azure AI Foundry resource",
362+
"Create a completion with the prompt \"What is Azure?\" using my Azure AI Foundry resource",
363+
"Create vector embeddings for my text using my Azure AI Foundry resource",
364+
"Generate embeddings for the text \"Azure OpenAI Service\" using my Azure AI Foundry resource"
363365
],
364366
"connect_azure_ai_foundry_agents": [
365-
"Query an agent in my AI foundry project"
367+
"Query an agent in my Azure AI foundry resource"
366368
],
367369
"query_and_evaluate_azure_ai_foundry_agents": [
368-
"Query and evaluate an agent in my AI Foundry project for task_adherence"
370+
"Query and evaluate an agent in my Azure AI Foundry resource for task_adherence"
369371
],
370372
"evaluate_azure_ai_foundry_agents": [
371373
"Evaluate the full query and response I got from my agent for task_adherence"
@@ -421,8 +423,10 @@
421423
"Show me the available regions for these resource types <resource_types>"
422424
],
423425
"get_azure_messaging_service_details": [
426+
"Get the details of my consumer group <consumer_group_name> in my event hub <event_hub_name>, namespace <namespace_name>, and resource group <resource_group_name>",
424427
"Get the details of my event hub <event_hub_name> in my namespace <namespace_name> and resource group <resource_group_name>",
425428
"Get the details of my namespace <namespace_name> in my resource group <resource_group_name>",
429+
"List all consumer groups in my event hub <event_hub_name> in namespace <namespace_name>",
426430
"List all Event Grid subscriptions in subscription <subscription>",
427431
"List all Event Grid topics in my subscription",
428432
"List all Event Grid topics in resource group <resource_group_name> in subscription <subscription>",
@@ -441,10 +445,13 @@
441445
"Show me the Event Grid topics in my subscription"
442446
],
443447
"edit_azure_data_analytics_resources": [
448+
"Create a new consumer group <consumer_group_name> in my event hub <event_hub_name>, namespace <namespace_name>, and resource group <resource_group_name>",
444449
"Create a new event hub <event_hub_name> in my namespace <namespace_name> and resource group <resource_group_name>",
445450
"Create an new namespace <namespace_name> in my resource group <resource_group_name>",
451+
"Delete my consumer group <consumer_group_name> in my event hub <event_hub_name>, namespace <namespace_name>, and resource group <resource_group_name>",
446452
"Delete my event hub <event_hub_name> in my namespace <namespace_name> and resource group <resource_group_name>",
447453
"Delete my namespace <namespace_name> in my resource group <resource_group_name>",
454+
"Update my consumer group <consumer_group_name> in my event hub <event_hub_name>, namespace <namespace_name>, and resource group <resource_group_name>",
448455
"Update my event hub <event_hub_name> in my namespace <namespace_name> and resource group <resource_group_name>",
449456
"Update my namespace <namespace_name> in my resource group <resource_group_name>"
450457
],

0 commit comments

Comments
 (0)