Skip to content

Commit 1772783

Browse files
authored
Migrate AppConfig tools to new tool design (#2871)
* Migrate AppConfig tools to new tool design * Add CHANGELOG entry
1 parent 76f73ff commit 1772783

24 files changed

Lines changed: 151 additions & 306 deletions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
changes:
2+
- section: "Breaking Changes"
3+
description: "Removed unused parameters from AppConfig tools. '--label' and '--label-filter' are now mutually excluside in appconfig_kv_get."

tools/Azure.Mcp.Tools.AppConfig/src/AppConfigSetup.cs

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

4-
using Azure.Mcp.Core.Services.Azure.Subscription;
54
using Azure.Mcp.Tools.AppConfig.Commands.Account;
65
using Azure.Mcp.Tools.AppConfig.Commands.KeyValue;
76
using Azure.Mcp.Tools.AppConfig.Commands.KeyValue.Lock;
87
using Azure.Mcp.Tools.AppConfig.Services;
98
using Microsoft.Extensions.DependencyInjection;
10-
using Microsoft.Extensions.DependencyInjection.Extensions;
119
using Microsoft.Mcp.Core.Areas;
1210
using Microsoft.Mcp.Core.Commands;
1311

tools/Azure.Mcp.Tools.AppConfig/src/Azure.Mcp.Tools.AppConfig.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,5 @@
1515
<PackageReference Include="Azure.ResourceManager" />
1616
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
1717
<PackageReference Include="ModelContextProtocol" />
18-
<PackageReference Include="System.CommandLine" />
1918
</ItemGroup>
2019
</Project>

tools/Azure.Mcp.Tools.AppConfig/src/Commands/Account/AccountListCommand.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
// Licensed under the MIT License.
33

44
using Azure.Mcp.Core.Commands.Subscription;
5+
using Azure.Mcp.Core.Services.Azure.Subscription;
56
using Azure.Mcp.Tools.AppConfig.Models;
67
using Azure.Mcp.Tools.AppConfig.Options.Account;
78
using Azure.Mcp.Tools.AppConfig.Services;
89
using Microsoft.Extensions.Logging;
910
using Microsoft.Mcp.Core.Commands;
10-
using Microsoft.Mcp.Core.Extensions;
1111
using Microsoft.Mcp.Core.Models.Command;
12-
using Microsoft.Mcp.Core.Models.Option;
1312

1413
namespace Azure.Mcp.Tools.AppConfig.Commands.Account;
1514

@@ -27,33 +26,14 @@ stores available in the specified subscription. Results include store names retu
2726
ReadOnly = true,
2827
Secret = false,
2928
LocalRequired = false)]
30-
public sealed class AccountListCommand(ILogger<AccountListCommand> logger, IAppConfigService appConfigService) : SubscriptionCommand<AccountListOptions>()
29+
public sealed class AccountListCommand(ILogger<AccountListCommand> logger, IAppConfigService appConfigService, ISubscriptionResolver subscriptionResolver)
30+
: SubscriptionCommand<AccountListOptions, AccountListCommand.AccountListCommandResult>(subscriptionResolver)
3131
{
3232
private readonly ILogger<AccountListCommand> _logger = logger;
3333
private readonly IAppConfigService _appConfigService = appConfigService;
3434

35-
protected override void RegisterOptions(Command command)
35+
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, AccountListOptions options, CancellationToken cancellationToken)
3636
{
37-
base.RegisterOptions(command);
38-
command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsOptional());
39-
}
40-
41-
protected override AccountListOptions BindOptions(ParseResult parseResult)
42-
{
43-
var options = base.BindOptions(parseResult);
44-
options.ResourceGroup = parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);
45-
return options;
46-
}
47-
48-
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
49-
{
50-
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
51-
{
52-
return context.Response;
53-
}
54-
55-
var options = BindOptions(parseResult);
56-
5737
try
5838
{
5939
var accounts = await _appConfigService.GetAppConfigAccounts(
@@ -74,5 +54,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
7454
return context.Response;
7555
}
7656

77-
internal record AccountListCommandResult(List<AppConfigurationAccount> Accounts, bool AreResultsTruncated);
57+
public sealed record AccountListCommandResult(List<AppConfigurationAccount> Accounts, bool AreResultsTruncated);
7858
}

tools/Azure.Mcp.Tools.AppConfig/src/Commands/BaseAppConfigCommand.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

tools/Azure.Mcp.Tools.AppConfig/src/Commands/KeyValue/BaseKeyValueCommand.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

tools/Azure.Mcp.Tools.AppConfig/src/Commands/KeyValue/KeyValueDeleteCommand.cs

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

4+
using Azure.Mcp.Core.Commands.Subscription;
5+
using Azure.Mcp.Core.Services.Azure.Subscription;
46
using Azure.Mcp.Tools.AppConfig.Options.KeyValue;
57
using Azure.Mcp.Tools.AppConfig.Services;
68
using Microsoft.Extensions.Logging;
@@ -24,26 +26,19 @@ Delete a key-value pair from an App Configuration store. This command removes th
2426
ReadOnly = false,
2527
Secret = false,
2628
LocalRequired = false)]
27-
public sealed class KeyValueDeleteCommand(ILogger<KeyValueDeleteCommand> logger, IAppConfigService appConfigService)
28-
: BaseKeyValueCommand<KeyValueDeleteOptions>()
29+
public sealed class KeyValueDeleteCommand(ILogger<KeyValueDeleteCommand> logger, IAppConfigService appConfigService, ISubscriptionResolver subscriptionResolver)
30+
: SubscriptionCommand<KeyValueDeleteOptions, KeyValueDeleteCommand.KeyValueDeleteCommandResult>(subscriptionResolver)
2931
{
3032
private readonly ILogger<KeyValueDeleteCommand> _logger = logger;
3133
private readonly IAppConfigService _appConfigService = appConfigService;
3234

33-
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
35+
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, KeyValueDeleteOptions options, CancellationToken cancellationToken)
3436
{
35-
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
36-
{
37-
return context.Response;
38-
}
39-
40-
var options = BindOptions(parseResult);
41-
4237
try
4338
{
4439
var existed = await _appConfigService.DeleteKeyValue(
45-
options.Account!,
46-
options.Key!,
40+
options.Account,
41+
options.Key,
4742
options.Subscription!,
4843
options.Tenant,
4944
options.RetryPolicy,
@@ -65,5 +60,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
6560
return context.Response;
6661
}
6762

68-
internal record KeyValueDeleteCommandResult(string? Key, string? Label, bool Existed, string Message);
63+
public sealed record KeyValueDeleteCommandResult(string? Key, string? Label, bool Existed, string Message);
6964
}
Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using Azure.Mcp.Core.Commands.Subscription;
5+
using Azure.Mcp.Core.Services.Azure.Subscription;
46
using Azure.Mcp.Tools.AppConfig.Models;
5-
using Azure.Mcp.Tools.AppConfig.Options;
67
using Azure.Mcp.Tools.AppConfig.Options.KeyValue;
78
using Azure.Mcp.Tools.AppConfig.Services;
89
using Microsoft.Extensions.Logging;
910
using Microsoft.Mcp.Core.Commands;
10-
using Microsoft.Mcp.Core.Extensions;
1111
using Microsoft.Mcp.Core.Models.Command;
12-
using Microsoft.Mcp.Core.Models.Option;
1312

1413
namespace Azure.Mcp.Tools.AppConfig.Commands.KeyValue;
1514

@@ -29,53 +28,31 @@ Gets key-values in an App Configuration store. This command can either retrieve
2928
ReadOnly = true,
3029
Secret = false,
3130
LocalRequired = false)]
32-
public sealed class KeyValueGetCommand(ILogger<KeyValueGetCommand> logger, IAppConfigService appConfigService)
33-
: BaseAppConfigCommand<KeyValueGetOptions>()
31+
public sealed class KeyValueGetCommand(ILogger<KeyValueGetCommand> logger, IAppConfigService appConfigService, ISubscriptionResolver subscriptionResolver)
32+
: SubscriptionCommand<KeyValueGetOptions, KeyValueGetCommand.KeyValueGetCommandResult>(subscriptionResolver)
3433
{
3534
private readonly ILogger<KeyValueGetCommand> _logger = logger;
3635
private readonly IAppConfigService _appConfigService = appConfigService;
3736

38-
protected override void RegisterOptions(Command command)
37+
public override void ValidateOptions(KeyValueGetOptions options, ValidationResult validationResult)
3938
{
40-
base.RegisterOptions(command);
41-
command.Options.Add(AppConfigOptionDefinitions.Key.AsOptional());
42-
command.Options.Add(AppConfigOptionDefinitions.Label);
43-
command.Options.Add(AppConfigOptionDefinitions.KeyFilter);
44-
command.Options.Add(AppConfigOptionDefinitions.LabelFilter);
45-
command.Validators.Add(result =>
39+
base.ValidateOptions(options, validationResult);
40+
if (!string.IsNullOrEmpty(options.Key) && !string.IsNullOrEmpty(options.KeyFilter))
4641
{
47-
var key = result.GetValueOrDefault<string>(AppConfigOptionDefinitions.Key.Name);
48-
var keyFilter = result.GetValueOrDefault(AppConfigOptionDefinitions.KeyFilter);
49-
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(keyFilter))
50-
{
51-
result.AddError("Cannot specify both --key and --key-filter options together. Use only one to get a specific key-value or to filter the list of key-values.");
52-
}
53-
});
54-
}
55-
56-
protected override KeyValueGetOptions BindOptions(ParseResult parseResult)
57-
{
58-
var options = base.BindOptions(parseResult);
59-
options.Key = parseResult.GetValueOrDefault<string>(AppConfigOptionDefinitions.Key.Name);
60-
options.Label = parseResult.GetValueOrDefault<string>(AppConfigOptionDefinitions.Label.Name);
61-
options.KeyFilter = parseResult.GetValueOrDefault<string>(AppConfigOptionDefinitions.KeyFilter.Name);
62-
options.LabelFilter = parseResult.GetValueOrDefault<string>(AppConfigOptionDefinitions.LabelFilter.Name);
63-
return options;
64-
}
65-
66-
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
67-
{
68-
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
42+
validationResult.Errors.Add("Cannot specify both --key and --key-filter options together. Use only one to get a specific key-value or to filter the list of key-values.");
43+
}
44+
if (!string.IsNullOrEmpty(options.Label) && !string.IsNullOrEmpty(options.LabelFilter))
6945
{
70-
return context.Response;
46+
validationResult.Errors.Add("Cannot specify both --label and --label-filter options together. Use only one to get a specific key-value or to filter the list of key-values.");
7147
}
48+
}
7249

73-
var options = BindOptions(parseResult);
74-
50+
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, KeyValueGetOptions options, CancellationToken cancellationToken)
51+
{
7552
try
7653
{
7754
var settings = await _appConfigService.GetKeyValues(
78-
options.Account!,
55+
options.Account,
7956
options.Subscription!,
8057
options.Key,
8158
options.Label,
@@ -96,5 +73,5 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
9673
return context.Response;
9774
}
9875

99-
internal record KeyValueGetCommandResult(List<KeyValueSetting> Settings);
76+
public sealed record KeyValueGetCommandResult(List<KeyValueSetting> Settings);
10077
}
Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
using Azure.Mcp.Tools.AppConfig.Options;
4+
using Azure.Mcp.Core.Commands.Subscription;
5+
using Azure.Mcp.Core.Services.Azure.Subscription;
56
using Azure.Mcp.Tools.AppConfig.Options.KeyValue;
67
using Azure.Mcp.Tools.AppConfig.Services;
78
using Microsoft.Extensions.Logging;
89
using Microsoft.Mcp.Core.Commands;
9-
using Microsoft.Mcp.Core.Extensions;
1010
using Microsoft.Mcp.Core.Models.Command;
1111

1212
namespace Azure.Mcp.Tools.AppConfig.Commands.KeyValue;
@@ -27,44 +27,20 @@ Set a key-value setting in an App Configuration store. This command creates or u
2727
ReadOnly = false,
2828
Secret = false,
2929
LocalRequired = false)]
30-
public sealed class KeyValueSetCommand(ILogger<KeyValueSetCommand> logger, IAppConfigService appConfigService)
31-
: BaseKeyValueCommand<KeyValueSetOptions>()
30+
public sealed class KeyValueSetCommand(ILogger<KeyValueSetCommand> logger, IAppConfigService appConfigService, ISubscriptionResolver subscriptionResolver)
31+
: SubscriptionCommand<KeyValueSetOptions, KeyValueSetCommand.KeyValueSetCommandResult>(subscriptionResolver)
3232
{
33-
private const string CommandTitle = "Set App Configuration Key-Value Setting";
3433
private readonly ILogger<KeyValueSetCommand> _logger = logger;
3534
private readonly IAppConfigService _appConfigService = appConfigService;
3635

37-
protected override void RegisterOptions(Command command)
36+
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, KeyValueSetOptions options, CancellationToken cancellationToken)
3837
{
39-
base.RegisterOptions(command);
40-
command.Options.Add(AppConfigOptionDefinitions.Value);
41-
command.Options.Add(AppConfigOptionDefinitions.Tags);
42-
}
43-
44-
protected override KeyValueSetOptions BindOptions(ParseResult parseResult)
45-
{
46-
var options = base.BindOptions(parseResult);
47-
options.Value = parseResult.GetValueOrDefault<string>(AppConfigOptionDefinitions.Value.Name);
48-
options.Tags = parseResult.GetValueOrDefault<string[]>(AppConfigOptionDefinitions.Tags.Name);
49-
return options;
50-
}
51-
52-
[McpServerTool(Destructive = true, ReadOnly = false, Title = CommandTitle)]
53-
public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
54-
{
55-
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
56-
{
57-
return context.Response;
58-
}
59-
60-
var options = BindOptions(parseResult);
61-
6238
try
6339
{
6440
await _appConfigService.SetKeyValue(
65-
options.Account!,
66-
options.Key!,
67-
options.Value!,
41+
options.Account,
42+
options.Key,
43+
options.Value,
6844
options.Subscription!,
6945
options.Tenant,
7046
options.RetryPolicy,
@@ -86,5 +62,5 @@ await _appConfigService.SetKeyValue(
8662
return context.Response;
8763
}
8864

89-
internal record KeyValueSetCommandResult(string? Key, string? Value, string? Label, string? ContentType = null, string[]? Tags = null);
65+
public sealed record KeyValueSetCommandResult(string? Key, string? Value, string? Label, string? ContentType = null, string[]? Tags = null);
9066
}

tools/Azure.Mcp.Tools.AppConfig/src/Commands/KeyValue/Lock/KeyValueLockSetCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,5 @@ await _appConfigService.SetKeyValueLockState(
6060
return context.Response;
6161
}
6262

63-
public record KeyValueLockSetCommandResult(string Key, string? Label, bool Locked);
63+
public sealed record KeyValueLockSetCommandResult(string Key, string? Label, bool Locked);
6464
}

0 commit comments

Comments
 (0)