Skip to content
Draft
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
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-Tables
/tools/Azure.Mcp.Tools.Tables/ @alzimmermsft @microsoft/azure-mcp

# ServiceLabel: %tools-Tables
# 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 @@ -662,6 +672,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 @@ -11,6 +11,7 @@ The Azure MCP Server updates automatically by default whenever a new release com
- 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 Azure Resource activity logs `azmcp_monitor_activitylog_list`. [[#720](https://github.com/microsoft/mcp/pull/720)]
- 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 @@ -1622,6 +1622,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 @@ -584,6 +584,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 Tables on Azure Storage or Cosmos DB";
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