Skip to content
Merged
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
5 changes: 2 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.3.0-preview.4" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables"
Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.6" />
<PackageVersion Include="Microsoft.HybridRow" Version="1.1.0-preview3" />
<PackageVersion Include="Microsoft.Azure.Cosmos.Aot" Version="0.1.2-preview.2" />
Expand Down Expand Up @@ -95,4 +94,4 @@
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<!-- Pin to 6.0.2 until https://github.com/coverlet-coverage/coverlet/issues/1737 is resolved -->
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CommandLine.Parsing;
using System.Diagnostics.CodeAnalysis;
using Azure.Mcp.Core.Helpers;
using Azure.Mcp.Core.Models.Option;
using Azure.Mcp.Core.Options;

Expand All @@ -20,9 +22,7 @@ protected override void RegisterOptions(Command command)
// This mirrors the prior behavior that preferred the explicit option but fell back to env var.
command.Validators.Add(commandResult =>
{
var hasOption = commandResult.HasOptionResult(OptionDefinitions.Common.Subscription.Name);
var hasEnv = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID"));
if (!hasOption && !hasEnv)
if (!HasSubscriptionAvailable(commandResult))
{
commandResult.AddError("Missing Required options: --subscription");
}
Expand All @@ -36,7 +36,7 @@ protected override TOptions BindOptions(ParseResult parseResult)
// Get subscription from command line option or fallback to environment variable
var subscriptionValue = parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.Subscription.Name);

var envSubscription = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
var envSubscription = EnvironmentHelpers.GetAzureSubscriptionId();
options.Subscription = (string.IsNullOrEmpty(subscriptionValue)
|| IsPlaceholder(subscriptionValue))
&& !string.IsNullOrEmpty(envSubscription)
Expand All @@ -46,6 +46,18 @@ protected override TOptions BindOptions(ParseResult parseResult)
return options;
}

/// <summary>
/// Checks if a subscription is available either from the command option or AZURE_SUBSCRIPTION_ID environment variable.
/// </summary>
/// <param name="commandResult">The command result to check for the subscription option.</param>
/// <returns>True if a subscription is available, false otherwise.</returns>
protected static bool HasSubscriptionAvailable(CommandResult commandResult)
{
var hasOption = commandResult.HasOptionResult(OptionDefinitions.Common.Subscription);
var hasEnv = !string.IsNullOrEmpty(EnvironmentHelpers.GetAzureSubscriptionId());
return hasOption || hasEnv;
}

private static bool IsPlaceholder(string value)
=> value.Contains("subscription") || value.Contains("default");
}
21 changes: 21 additions & 0 deletions core/Azure.Mcp.Core/src/Helpers/EnvironmentVariableHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Azure.Mcp.Core.Helpers
{
public static class EnvironmentHelpers
{
private const string AzureSubscriptionIdEnvironmentVariable = "AZURE_SUBSCRIPTION_ID";

public static bool GetEnvironmentVariableAsBool(string envVarName)
{
return Environment.GetEnvironmentVariable(envVarName) switch
Expand All @@ -16,5 +18,24 @@ public static bool GetEnvironmentVariableAsBool(string envVarName)
_ => false
};
}

/// <summary>
/// Gets the Azure subscription ID from the AZURE_SUBSCRIPTION_ID environment variable.
/// </summary>
/// <returns>The subscription ID if available, null otherwise.</returns>
public static string? GetAzureSubscriptionId()
{
return Environment.GetEnvironmentVariable(AzureSubscriptionIdEnvironmentVariable);
}

/// <summary>
/// Sets the AZURE_SUBSCRIPTION_ID environment variable.
/// This method is primarily intended for testing scenarios.
/// </summary>
/// <param name="subscriptionId">The subscription ID to set, or null to clear the variable.</param>
public static void SetAzureSubscriptionId(string? subscriptionId)
{
Environment.SetEnvironmentVariable(AzureSubscriptionIdEnvironmentVariable, subscriptionId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System.CommandLine;
using Azure.Mcp.Core.Helpers;
using Azure.Mcp.Core.Models.Command;
using Azure.Mcp.Core.Options;
using Azure.Mcp.Tools.Storage.Commands.Account;
Expand Down Expand Up @@ -40,9 +41,8 @@ public SubscriptionCommandTests()
public void Validate_WithEnvironmentVariableOnly_PassesValidation()
{
// Arrange
var originalValue = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", "env-subs");

var originalValue = EnvironmentHelpers.GetAzureSubscriptionId();
EnvironmentHelpers.SetAzureSubscriptionId("env-subs");
try
{
var parseResult = _commandDefinition.Parse([]);
Expand All @@ -53,16 +53,16 @@ public void Validate_WithEnvironmentVariableOnly_PassesValidation()
finally
{
// Cleanup
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", originalValue);
EnvironmentHelpers.SetAzureSubscriptionId(originalValue);
}
}

[Fact]
public async Task ExecuteAsync_WithEnvironmentVariableOnly_CallsServiceWithCorrectSubscription()
{
// Arrange
var originalValue = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", "env-subs");
var originalValue = EnvironmentHelpers.GetAzureSubscriptionId();
EnvironmentHelpers.SetAzureSubscriptionId("env-subs");

try
{
Expand Down Expand Up @@ -97,16 +97,16 @@ public async Task ExecuteAsync_WithEnvironmentVariableOnly_CallsServiceWithCorre
finally
{
// Cleanup
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", originalValue);
EnvironmentHelpers.SetAzureSubscriptionId(originalValue);
}
}

[Fact]
public async Task ExecuteAsync_WithBothOptionAndEnvironmentVariable_PrefersOption()
{
// Arrange
var originalValue = Environment.GetEnvironmentVariable("AZURE_SUBSCRIPTION_ID");
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", "env-subs");
var originalValue = EnvironmentHelpers.GetAzureSubscriptionId();
EnvironmentHelpers.SetAzureSubscriptionId("env-subs");

try
{
Expand Down Expand Up @@ -146,7 +146,7 @@ public async Task ExecuteAsync_WithBothOptionAndEnvironmentVariable_PrefersOptio
finally
{
// Cleanup
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", originalValue);
EnvironmentHelpers.SetAzureSubscriptionId(originalValue);
}
}
}
7 changes: 7 additions & 0 deletions docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,13 @@ azmcp deploy plan get --workspace-folder <workspace-folder> \
# List all Event Grid topics in a subscription or resource group
azmcp eventgrid topic list --subscription <subscription> \
[--resource-group <resource-group>]


# List all Event Grid subscriptions in a subscription, resource group, or topic
azmcp eventgrid subscription list --subscription <subscription> \
[--resource-group <resource-group>] \
[--topic <topic>]
[--location <location>]
```

### Azure Function App Operations
Expand Down
7 changes: 7 additions & 0 deletions docs/e2eTestPrompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ This file contains prompts used for end-to-end testing to ensure each tool is in
| azmcp_eventgrid_topic_list | Show me the Event Grid topics in my subscription |
| azmcp_eventgrid_topic_list | List all Event Grid topics in subscription <subscription> |
| azmcp_eventgrid_topic_list | List all Event Grid topics in resource group <resource_group_name> in subscription <subscription> |
| azmcp_eventgrid_subscription_list | Show me all Event Grid subscriptions for topic <topic_name> |
| azmcp_eventgrid_subscription_list | List Event Grid subscriptions for topic <topic_name> in subscription <subscription> |
| azmcp_eventgrid_subscription_list | List Event Grid subscriptions for topic <topic_name> in resource group <resource_group_name> |
| azmcp_eventgrid_subscription_list | Show all Event Grid subscriptions in my subscription |
Comment thread
anannya03 marked this conversation as resolved.
| azmcp_eventgrid_subscription_list | List all Event Grid subscriptions in subscription <subscription> |
Comment thread
anannya03 marked this conversation as resolved.
| azmcp_eventgrid_subscription_list | Show Event Grid subscriptions in resource group <resource_group_name> in subscription <subscription> |
| azmcp_eventgrid_subscription_list | List Event Grid subscriptions for subscription <subscription> in location <location> |

## Azure Function App

Expand Down
8 changes: 7 additions & 1 deletion servers/Azure.Mcp.Server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ The Azure MCP Server supercharges your agents with Azure context. Here are some

* "List all Event Grid topics in subscription 'my-subscription'"
* "Show me the Event Grid topics in my subscription"
* "List all Event Grid topics in resource group 'my-resource-group' in my subscription"
* "List all Event Grid topics in resource group 'my-resourcegroup' in my subscription"
* "List Event Grid subscriptions for topic 'my-topic' in resource group 'my-resourcegroup'"
* "List Event Grid subscriptions for topic 'my-topic' in subscription 'my-subscription'"
* "List Event Grid Subscriptions in subscription 'my-subscription'"
* "List Event Grid subscriptions for topic 'my-topic' in location 'my-location'"

### ⚡ Azure Managed Lustre

Expand Down Expand Up @@ -211,6 +215,8 @@ The Azure MCP Server supercharges your agents with Azure context. Here are some
* List Event Grid topics in subscription or resource group
* View topic configuration and status information
* Access endpoint and key details for event publishing
* List Event Grid subscriptions with filtering by topic name, resource group, and location
* View subscription details including destination endpoints and retry policies

### 🧮 Azure Foundry

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.CommandLine;
using System.Text.Json;
using Azure.Mcp.Core.Helpers;
using Azure.Mcp.Core.Models.Command;
using Azure.Mcp.Core.Options;
using Azure.Mcp.Tools.Acr.Commands;
Expand Down Expand Up @@ -52,7 +53,7 @@ public void Constructor_InitializesCommandCorrectly()
public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldSucceed)
{
// Ensure environment variable fallback does not interfere with validation tests
Environment.SetEnvironmentVariable("AZURE_SUBSCRIPTION_ID", null);
EnvironmentHelpers.SetAzureSubscriptionId(null);
// Arrange
if (shouldSucceed)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.Mcp.Tools.EventGrid.Commands.Subscription;
using Azure.Mcp.Tools.EventGrid.Commands.Topic;

namespace Azure.Mcp.Tools.EventGrid.Commands;

[JsonSerializable(typeof(SubscriptionListCommand.SubscriptionListCommandResult))]
[JsonSerializable(typeof(TopicListCommand.TopicListCommandResult))]
[JsonSerializable(typeof(EventGridSubscriptionInfo))]
[JsonSerializable(typeof(EventGridTopicInfo))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
internal sealed partial class EventGridJsonContext : JsonSerializerContext
{
}
Loading