Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 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
6 changes: 6 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@
# ServiceLabel: %tools-Storage
# ServiceOwners: @alzimmermsft @jongio

# PRLabel: %tools-Table
/tools/Azure.Mcp.Tools.Table/ @alzimmermsft @microsoft/azure-mcp

# ServiceLabel: %tools-Table
# ServiceOwners: @alzimmermsft

# PRLabel: %tools-Authorization
/tools/Azure.Mcp.Tools.Authorization/ @vurhanau @jongio @xiangyan99 @microsoft/azure-mcp

Expand Down
162 changes: 108 additions & 54 deletions AzureMcp.sln

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageVersion Include="Azure.Bicep.Types" Version="0.6.1" />
<PackageVersion Include="Azure.Core" Version="1.48.0" />
<PackageVersion Include="Azure.Data.AppConfiguration" Version="1.6.1" />
<PackageVersion Include="Azure.Data.Tables" Version="12.11.0" />
<PackageVersion Include="Azure.Identity" Version="1.16.0" />
<PackageVersion Include="Azure.Developer.LoadTesting" Version="1.0.2" />
<PackageVersion Include="Azure.Identity.Broker" Version="1.3.0" />
Expand Down
30 changes: 23 additions & 7 deletions eng/tools/ToolDescriptionEvaluator/prompts.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@
"Create an immutable ledger entry in <ledger_name> with content {\"audit\": \"log\"}",
"Write an entry to confidential ledger <ledger_name>"
],
"azmcp_confidentialledger_entries_get": [
"Get entry from Confidential Ledger for transaction <transaction_id> on ledger <ledger_name>",
"Get transaction <transaction_id> from ledger <ledger_name>"
],
"azmcp_cosmos_account_list": [
"List all cosmosdb accounts in my subscription",
"Show me my cosmosdb accounts",
Expand Down Expand Up @@ -411,18 +415,24 @@
"azmcp_grafana_list": [
"List all Azure Managed Grafana in one subscription"
],
"azmcp_azuremanagedlustre_filesystem_list": [
"azmcp_managedlustre_filesystem_create": [
"Create an Azure Managed Lustre filesystem with name <filesystem_name>, size <filesystem_size>, SKU <sku>, and subnet <subnet_id> for availability zone <zone> in location <location>. Maintenance should occur on <maintenance_window_day> at <maintenance_window_time>"
],
"azmcp_managedlustre_filesystem_list": [
"List the Azure Managed Lustre filesystems in my subscription <subscription_name>",
"List the Azure Managed Lustre filesystems in my resource group <resource_group_name>"
],
"azmcp_azuremanagedlustre_filesystem_sku_get": [
"List the Azure Managed Lustre SKUs available in <location>"
"azmcp_managedlustre_filesystem_sku_get": [
"List the Azure Managed Lustre SKUs available in location <location>"
],
"azmcp_azuremanagedlustre_filesystem_subnetsize_ask": [
"Tell me how many IP addresses I need for <filesystem_size> of <amlfs_sku>"
"azmcp_managedlustre_filesystem_subnetsize_ask": [
"Tell me how many IP addresses I need for an Azure Managed Lustre filesystem of size <filesystem_size> using the SKU <sku>"
],
"azmcp_azuremanagedlustre_filesystem_subnetsize_validate": [
"Validate if <subnet_id> can host <filesystem_size> of <amlfs_sku>"
"azmcp_managedlustre_filesystem_subnetsize_validate": [
"Validate if the network <subnet_id> can host Azure Managed Lustre filesystem of size <filesystem_size> using the SKU <sku>"
],
"azmcp_managedlustre_filesystem_update": [
"Update the maintenance window of the Azure Managed Lustre filesystem <filesystem_name> to <maintenance_window_day> at <maintenance_window_time>"
],
"azmcp_marketplace_product_get": [
"Get details about marketplace product <product_name>"
Expand Down Expand Up @@ -659,6 +669,12 @@
"What is my current subscription?",
"What subscriptions do I have?"
],
"azmcp_tables_list": [
"List all tables in the storage account <storage-account>",
"Show me the tables in the storage account <storage-account>",
"List all tables in the Cosmos DB account <cosmosdb-account>",
"Show me the tables in the Cosmos DB account <cosmosdb-account>"
],
"azmcp_azureterraformbestpractices_get": [
"Fetch the Azure Terraform best practices",
"Show me the Azure Terraform best practices and generate code sample to get a secret from Azure Key Vault"
Expand Down
3,194 changes: 1,669 additions & 1,525 deletions eng/tools/ToolDescriptionEvaluator/results.md

Large diffs are not rendered by default.

2,284 changes: 1,209 additions & 1,075 deletions eng/tools/ToolDescriptionEvaluator/tools.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions servers/Azure.Mcp.Server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The Azure MCP Server updates automatically by default whenever a new release com
- Added `--tool` option to start Azure MCP server with only specific tools by name, providing fine-grained control over tool exposure. This option switches server mode to all automatically. The `--namespace` and `--tool` options cannot be used together. [[#685](https://github.com/microsoft/mcp/issues/685)]
- Updated `ToolArea` telemetry field to be populated for namespace (and intent/learn) calls. [[#739](https://github.com/microsoft/mcp/pull/739)]
- Added support for Azure Confidential Ledger with the command `azmcp_confidentialledger_entries_get` for getting ledger entries identified by their ID. [[#705](https://github.com/microsoft/mcp/pull/723)]
- Added support for listing tables in Azure Storage and CosmosDB via command `azmcp_tables_list`. [[#743](https://github.com/microsoft/mcp/pull/743)]

### Breaking Changes

Expand Down
10 changes: 10 additions & 0 deletions servers/Azure.Mcp.Server/docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,16 @@ azmcp storage blob upload --subscription <subscription> \
--local-file-path <path-to-local-file>
```

### Azure Tables Operations

```bash
# List tables in a Storage or Cosmos DB account
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp tables list --subscription <subscription> \
[--storage-account <storage-account>] \
[--cosmosdb-account <cosmosdb-account>]
```

### Azure Subscription Management

```bash
Expand Down
9 changes: 9 additions & 0 deletions servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,15 @@ This file contains prompts used for end-to-end testing to ensure each tool is in
| azmcp_subscription_list | What is my current subscription? |
| azmcp_subscription_list | What subscriptions do I have? |

## Azure Tables

| Tool Name | Test Prompt |
|:----------|:----------|
| azmcp_tables_list | List all tables in the storage account <storage-account> |
| azmcp_tables_list | Show me the tables in the storage account <storage-account> |
| azmcp_tables_list | List all tables in the Cosmos DB account <cosmosdb-account> |
| azmcp_tables_list | Show me the tables in the Cosmos DB account <cosmosdb-account> |

## Azure Terraform Best Practices

| Tool Name | Test Prompt |
Expand Down
1 change: 1 addition & 0 deletions servers/Azure.Mcp.Server/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private static IAreaSetup[] RegisterAreas()
new Azure.Mcp.Tools.SignalR.SignalRSetup(),
new Azure.Mcp.Tools.Sql.SqlSetup(),
new Azure.Mcp.Tools.Storage.StorageSetup(),
new Azure.Mcp.Tools.Tables.TablesSetup(),
new Azure.Mcp.Tools.VirtualDesktop.VirtualDesktopSetup(),
new Azure.Mcp.Tools.Workbooks.WorkbooksSetup(),
#if !BUILD_NATIVE
Expand Down
42 changes: 42 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/Azure.Mcp.Tools.Tables.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Table", "src\Azure.Mcp.Tools.Table.csproj", "{B7A97E82-2E1D-BE3A-2A7C-A90F3165AACD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Table.LiveTests", "tests\Azure.Mcp.Tools.Table.LiveTests\Azure.Mcp.Tools.Table.LiveTests.csproj", "{83DD8707-A48C-1D6B-0D9F-63BD93180599}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Mcp.Tools.Table.UnitTests", "tests\Azure.Mcp.Tools.Table.UnitTests\Azure.Mcp.Tools.Table.UnitTests.csproj", "{C0756D40-AADD-FDA6-49C2-973096FF795D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7A97E82-2E1D-BE3A-2A7C-A90F3165AACD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7A97E82-2E1D-BE3A-2A7C-A90F3165AACD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7A97E82-2E1D-BE3A-2A7C-A90F3165AACD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7A97E82-2E1D-BE3A-2A7C-A90F3165AACD}.Release|Any CPU.Build.0 = Release|Any CPU
{83DD8707-A48C-1D6B-0D9F-63BD93180599}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83DD8707-A48C-1D6B-0D9F-63BD93180599}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83DD8707-A48C-1D6B-0D9F-63BD93180599}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83DD8707-A48C-1D6B-0D9F-63BD93180599}.Release|Any CPU.Build.0 = Release|Any CPU
{C0756D40-AADD-FDA6-49C2-973096FF795D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0756D40-AADD-FDA6-49C2-973096FF795D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0756D40-AADD-FDA6-49C2-973096FF795D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0756D40-AADD-FDA6-49C2-973096FF795D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{83DD8707-A48C-1D6B-0D9F-63BD93180599} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{C0756D40-AADD-FDA6-49C2-973096FF795D} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3BE8B60B-F7A1-4780-A64D-170B629FEF04}
EndGlobalSection
EndGlobal
7 changes: 7 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Azure.Mcp.Tools.Tables.UnitTests")]
[assembly: InternalsVisibleTo("Azure.Mcp.Tools.Tables.LiveTests")]
20 changes: 20 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Azure.Mcp.Tools.Tables.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="**\Resources\*.txt" />
<EmbeddedResource Include="**\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Azure.Mcp.Core\src\Azure.Mcp.Core.csproj" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="Azure.Data.Tables" />
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="System.CommandLine" />
</ItemGroup>
</Project>
42 changes: 42 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Commands/BaseTablesCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Azure.Mcp.Core.Commands;
using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Extensions;
using Azure.Mcp.Tools.Tables.Options;

namespace Azure.Mcp.Tools.Tables.Commands;

public abstract class BaseTablesCommand<[DynamicallyAccessedMembers(TrimAnnotations.CommandAnnotations)] T> : SubscriptionCommand<T>
where T : BaseTablesOptions, new()
{
protected override void RegisterOptions(Command command)
{
base.RegisterOptions(command);
command.Options.Add(TablesOptionDefinitions.Account);
command.Options.Add(TablesOptionDefinitions.CosmosDbAccount);
command.Validators.Add(commandResult =>
{
var storageAccount = commandResult.GetValueOrDefault<string>(TablesOptionDefinitions.Account.Name);
var cosmosDbAccount = commandResult.GetValueOrDefault<string>(TablesOptionDefinitions.CosmosDbAccount.Name);
if (string.IsNullOrWhiteSpace(storageAccount) && string.IsNullOrWhiteSpace(cosmosDbAccount))
{
commandResult.AddError($"One of --{TablesOptionDefinitions.StorageAccountName} or --{TablesOptionDefinitions.CosmosDbAccountName} must be provided.");
}
else if (!string.IsNullOrWhiteSpace(storageAccount) && !string.IsNullOrWhiteSpace(cosmosDbAccount))
{
commandResult.AddError($"Only one of --{TablesOptionDefinitions.StorageAccountName} or --{TablesOptionDefinitions.CosmosDbAccountName} can be provided.");
}
});
}

protected override T BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.StorageAccount = parseResult.GetValueOrDefault<string>(TablesOptionDefinitions.Account.Name);
options.CosmosDbAccount = parseResult.GetValueOrDefault<string>(TablesOptionDefinitions.CosmosDbAccount.Name);
return options;
}
}
12 changes: 12 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Commands/TablesJsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace Azure.Mcp.Tools.Tables.Commands;

[JsonSerializable(typeof(TablesListCommand.TablesListCommandResult))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault)]
internal sealed partial class TablesJsonContext : JsonSerializerContext
{
}
65 changes: 65 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Commands/TablesListCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Commands;
using Azure.Mcp.Tools.Tables.Options;
using Azure.Mcp.Tools.Tables.Services;
using Microsoft.Extensions.Logging;

namespace Azure.Mcp.Tools.Tables.Commands;

public sealed class TablesListCommand(ILogger<TablesListCommand> logger) : BaseTablesCommand<TablesListOptions>()
{
private const string CommandTitle = "List Azure Table storage tables";
private readonly ILogger<TablesListCommand> _logger = logger;

public override string Name => "list";

public override string Description => "List all tables in a Storage or Cosmos DB account.";

public override string Title => CommandTitle;

public override ToolMetadata Metadata => new()
{
Destructive = false,
Idempotent = true,
OpenWorld = false,
ReadOnly = true,
LocalRequired = false,
Secret = false
};

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

try
{
var account = options.StorageAccount ?? options.CosmosDbAccount;
var tablesService = context.GetService<ITablesService>();
var tables = await tablesService.ListTables(
account!,
!string.IsNullOrWhiteSpace(options.CosmosDbAccount),
options.Subscription!,
options.Tenant,
options.RetryPolicy);

context.Response.Results = ResponseResult.Create(new(tables ?? []), TablesJsonContext.Default.TablesListCommandResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error listing tables. StorageAccount: {StorageAccount}, CosmosDbAccount: {CosmosDbAccount}.",
options.StorageAccount, options.CosmosDbAccount);
HandleException(context, ex);
}

return context.Response;
}

internal record TablesListCommandResult(List<string> Tables);
}
5 changes: 5 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

global using System.CommandLine;
global using Azure.Mcp.Core.Models.Command;
12 changes: 12 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Options/BaseTablesOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Options;

namespace Azure.Mcp.Tools.Tables.Options;

public class BaseTablesOptions : SubscriptionOptions
{
public string? StorageAccount { get; set; }
public string? CosmosDbAccount { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.Mcp.Tools.Tables.Options;

public class TablesListOptions : BaseTablesOptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.Mcp.Tools.Tables.Options;

public static class TablesOptionDefinitions
{
public const string StorageAccountName = "storage-account";
public const string CosmosDbAccountName = "cosmosdb-account";

public static readonly Option<string> Account = new($"--{StorageAccountName}")
{
Description = "The name of the Azure Storage account (e.g., 'mystorageaccount').",
};

public static readonly Option<string> CosmosDbAccount = new($"--{CosmosDbAccountName}")
{
Description = "The name of the Cosmos DB account (e.g., 'mycosmosdbaccount').",
};
}
16 changes: 16 additions & 0 deletions tools/Azure.Mcp.Tools.Tables/src/Services/ITablesService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Options;

namespace Azure.Mcp.Tools.Tables.Services;

public interface ITablesService
{
Task<List<string>> ListTables(
string account,
bool isCosmosDb,
string subscription,
string? tenant = null,
RetryPolicyOptions? retryPolicy = null);
}
Loading