diff --git a/servers/Azure.Mcp.Server/changelog-entries/1782416793886.yaml b/servers/Azure.Mcp.Server/changelog-entries/1782416793886.yaml
new file mode 100644
index 0000000000..9e40fa4b10
--- /dev/null
+++ b/servers/Azure.Mcp.Server/changelog-entries/1782416793886.yaml
@@ -0,0 +1,3 @@
+changes:
+ - section: "Breaking Changes"
+ description: "Removed unused parameters from Search tools."
diff --git a/tools/Azure.Mcp.Tools.Search/src/Azure.Mcp.Tools.Search.csproj b/tools/Azure.Mcp.Tools.Search/src/Azure.Mcp.Tools.Search.csproj
index febeb8c15f..2f89e7f9a0 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Azure.Mcp.Tools.Search.csproj
+++ b/tools/Azure.Mcp.Tools.Search/src/Azure.Mcp.Tools.Search.csproj
@@ -7,7 +7,7 @@
-
+
@@ -16,6 +16,5 @@
-
\ No newline at end of file
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexGetCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexGetCommand.cs
index e62668d596..8ce56215c2 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexGetCommand.cs
@@ -2,14 +2,11 @@
// Licensed under the MIT License.
using Azure.Mcp.Tools.Search.Models;
-using Azure.Mcp.Tools.Search.Options;
using Azure.Mcp.Tools.Search.Options.Index;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.Search.Commands.Index;
@@ -28,39 +25,18 @@ namespace Azure.Mcp.Tools.Search.Commands.Index;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class IndexGetCommand(ILogger logger, ISearchService searchService) : GlobalCommand()
+public sealed class IndexGetCommand(ILogger logger, ISearchService searchService)
+ : AuthenticatedCommand
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, IndexGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(SearchOptionDefinitions.Service);
- command.Options.Add(SearchOptionDefinitions.Index.AsOptional());
- }
-
- protected override IndexGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Service = parseResult.GetValueOrDefault(SearchOptionDefinitions.Service.Name);
- options.Index = parseResult.GetValueOrDefault(SearchOptionDefinitions.Index.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
try
{
var indexes = await _searchService.GetIndexDetails(
- options.Service!,
+ options.Service,
options.Index,
options.RetryPolicy,
cancellationToken);
@@ -76,5 +52,5 @@ public override async Task ExecuteAsync(CommandContext context,
return context.Response;
}
- internal sealed record IndexGetCommandResult(List Indexes);
+ public sealed record IndexGetCommandResult(List Indexes);
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexQueryCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexQueryCommand.cs
index dc66c60314..4458b4895d 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexQueryCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Index/IndexQueryCommand.cs
@@ -1,12 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using Azure.Mcp.Tools.Search.Options;
+using System.Text.Json;
using Azure.Mcp.Tools.Search.Options.Index;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
namespace Azure.Mcp.Tools.Search.Commands.Index;
@@ -25,43 +24,20 @@ namespace Azure.Mcp.Tools.Search.Commands.Index;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class IndexQueryCommand(ILogger logger, ISearchService searchService) : GlobalCommand()
+public sealed class IndexQueryCommand(ILogger logger, ISearchService searchService)
+ : AuthenticatedCommand>
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, IndexQueryOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(SearchOptionDefinitions.Service);
- command.Options.Add(SearchOptionDefinitions.Index);
- command.Options.Add(SearchOptionDefinitions.Query);
- }
-
- protected override IndexQueryOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Service = parseResult.GetValueOrDefault(SearchOptionDefinitions.Service.Name);
- options.Index = parseResult.GetValueOrDefault(SearchOptionDefinitions.Index.Name);
- options.Query = parseResult.GetValueOrDefault(SearchOptionDefinitions.Query.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
try
{
var results = await _searchService.QueryIndex(
- options.Service!,
- options.Index!,
- options.Query!,
+ options.Service,
+ options.Index,
+ options.Query,
options.RetryPolicy,
cancellationToken);
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseGetCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseGetCommand.cs
index 5b854dfea9..8899a7cf0b 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseGetCommand.cs
@@ -1,14 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using Azure.Mcp.Tools.Search.Options;
using Azure.Mcp.Tools.Search.Options.Knowledge;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.Search.Commands.Knowledge;
@@ -30,38 +27,17 @@ Gets the details of Azure AI Search knowledge bases. Knowledge bases encapsulate
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class KnowledgeBaseGetCommand(ILogger logger, ISearchService searchService) : GlobalCommand()
+public sealed class KnowledgeBaseGetCommand(ILogger logger, ISearchService searchService)
+ : AuthenticatedCommand
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, KnowledgeBaseGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(SearchOptionDefinitions.Service);
- command.Options.Add(SearchOptionDefinitions.KnowledgeBase.AsOptional());
- }
-
- protected override KnowledgeBaseGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Service = parseResult.GetValueOrDefault(SearchOptionDefinitions.Service.Name);
- options.KnowledgeBase = parseResult.GetValueOrDefault(SearchOptionDefinitions.KnowledgeBase.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
try
{
- var bases = await _searchService.ListKnowledgeBases(options.Service!, options.KnowledgeBase, options.RetryPolicy, cancellationToken);
+ var bases = await _searchService.ListKnowledgeBases(options.Service, options.KnowledgeBase, options.RetryPolicy, cancellationToken);
context.Response.Results = ResponseResult.Create(new(bases ?? []), SearchJsonContext.Default.KnowledgeBaseGetCommandResult);
}
catch (Exception ex)
@@ -73,5 +49,5 @@ public override async Task ExecuteAsync(CommandContext context,
return context.Response;
}
- internal sealed record KnowledgeBaseGetCommandResult(List KnowledgeBases);
+ public sealed record KnowledgeBaseGetCommandResult(List KnowledgeBases);
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseRetrieveCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseRetrieveCommand.cs
index 4df07b3431..5aedd6267f 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseRetrieveCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeBaseRetrieveCommand.cs
@@ -2,15 +2,11 @@
// Licensed under the MIT License.
using System.Net;
-using Azure.Mcp.Core.Commands;
-using Azure.Mcp.Tools.Search.Options;
using Azure.Mcp.Tools.Search.Options.Knowledge;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.Search.Commands.Knowledge;
@@ -34,70 +30,46 @@ namespace Azure.Mcp.Tools.Search.Commands.Knowledge;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class KnowledgeBaseRetrieveCommand(ILogger logger, ISearchService searchService) : GlobalCommand()
+public sealed class KnowledgeBaseRetrieveCommand(ILogger logger, ISearchService searchService)
+ : AuthenticatedCommand
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override void ValidateOptions(KnowledgeBaseRetrieveOptions options, ValidationResult validationResult)
{
- base.RegisterOptions(command);
- command.Options.Add(SearchOptionDefinitions.Service);
- command.Options.Add(SearchOptionDefinitions.KnowledgeBase);
- command.Options.Add(SearchOptionDefinitions.KnowledgeQuery.AsOptional());
- command.Options.Add(SearchOptionDefinitions.Messages.AsOptional());
- command.Validators.Add(commandResult =>
+ base.ValidateOptions(options, validationResult);
+
+ if (string.IsNullOrEmpty(options.Query) && (options.Messages == null || options.Messages.Length == 0))
{
- var query = commandResult.GetValueOrDefault(SearchOptionDefinitions.KnowledgeQuery.Name);
- var messages = commandResult.GetValueOrDefault(SearchOptionDefinitions.Messages.Name) ?? [];
- if (string.IsNullOrEmpty(query) && messages.Length == 0)
- {
- commandResult.AddError("Either --query or at least one --messages entry must be provided.");
- }
- else if (!string.IsNullOrEmpty(query) && messages.Length > 0)
- {
- commandResult.AddError("Specifying both --query and --messages is not allowed.");
- }
+ validationResult.Errors.Add("Either --query or at least one --messages entry must be provided.");
+ }
+ else if (!string.IsNullOrEmpty(options.Query) && options.Messages is { Length: > 0 })
+ {
+ validationResult.Errors.Add("Specifying both --query and --messages is not allowed.");
+ }
- if (messages.Length > 0)
+ if (options.Messages is { Length: > 0 })
+ {
+ foreach ((var index, var message) in options.Messages.Index())
{
- foreach ((var index, var message) in messages.Index())
+ try
{
- try
- {
- ParseMessage(message);
- }
- catch (ArgumentException ex)
- {
- commandResult.AddError($"Message {index}: {ex.Message}");
- continue;
- }
+ ParseMessage(message);
+ }
+ catch (ArgumentException ex)
+ {
+ validationResult.Errors.Add($"Message {index}: {ex.Message}");
+ continue;
}
}
- });
- }
-
- protected override KnowledgeBaseRetrieveOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Service = parseResult.GetValueOrDefault(SearchOptionDefinitions.Service.Name);
- options.KnowledgeBase = parseResult.GetValueOrDefault(SearchOptionDefinitions.KnowledgeBase.Name);
- options.Query = parseResult.GetValueOrDefault(SearchOptionDefinitions.KnowledgeQuery.Name);
- options.Messages = parseResult.GetValueOrDefault(SearchOptionDefinitions.Messages.Name) ?? [];
- return options;
+ }
}
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
+ public override async Task ExecuteAsync(CommandContext context, KnowledgeBaseRetrieveOptions options, CancellationToken cancellationToken)
{
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
List<(string role, string message)>? parsedMessages = null;
- if (options.Messages.Length > 0)
+ if (options.Messages is { Length: > 0 })
{
try
{
@@ -113,7 +85,7 @@ public override async Task ExecuteAsync(CommandContext context,
try
{
- var result = await _searchService.RetrieveFromKnowledgeBase(options.Service!, options.KnowledgeBase!, options.Query, parsedMessages, options.RetryPolicy, cancellationToken);
+ var result = await _searchService.RetrieveFromKnowledgeBase(options.Service, options.KnowledgeBase, options.Query, parsedMessages, options.RetryPolicy, cancellationToken);
context.Response.Results = ResponseResult.Create(new(result), SearchJsonContext.Default.KnowledgeBaseRetrieveCommandResult);
}
catch (Exception ex)
@@ -145,5 +117,5 @@ internal static (string role, string content) ParseMessage(string message)
return (role, content);
}
- internal sealed record KnowledgeBaseRetrieveCommandResult(string RetrievalResult);
+ public sealed record KnowledgeBaseRetrieveCommandResult(string RetrievalResult);
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeSourceGetCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeSourceGetCommand.cs
index a899ed731d..5e6d06ca83 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeSourceGetCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Knowledge/KnowledgeSourceGetCommand.cs
@@ -1,14 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using Azure.Mcp.Tools.Search.Options;
using Azure.Mcp.Tools.Search.Options.Knowledge;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.Search.Commands.Knowledge;
@@ -32,38 +29,17 @@ within the specified service.
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class KnowledgeSourceGetCommand(ILogger logger, ISearchService searchService) : GlobalCommand()
+public sealed class KnowledgeSourceGetCommand(ILogger logger, ISearchService searchService)
+ : AuthenticatedCommand
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, KnowledgeSourceGetOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(SearchOptionDefinitions.Service);
- command.Options.Add(SearchOptionDefinitions.KnowledgeSource.AsOptional());
- }
-
- protected override KnowledgeSourceGetOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.Service = parseResult.GetValueOrDefault(SearchOptionDefinitions.Service.Name);
- options.KnowledgeSource = parseResult.GetValueOrDefault(SearchOptionDefinitions.KnowledgeSource.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
try
{
- var sources = await _searchService.ListKnowledgeSources(options.Service!, options.KnowledgeSource, options.RetryPolicy, cancellationToken);
+ var sources = await _searchService.ListKnowledgeSources(options.Service, options.KnowledgeSource, options.RetryPolicy, cancellationToken);
context.Response.Results = ResponseResult.Create(new(sources ?? []), SearchJsonContext.Default.KnowledgeSourceGetCommandResult);
}
catch (Exception ex)
@@ -75,5 +51,5 @@ public override async Task ExecuteAsync(CommandContext context,
return context.Response;
}
- internal sealed record KnowledgeSourceGetCommandResult(List KnowledgeSources);
+ public sealed record KnowledgeSourceGetCommandResult(List KnowledgeSources);
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/SearchJsonContext.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/SearchJsonContext.cs
index f034e5ff73..ef863c05a9 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/SearchJsonContext.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/SearchJsonContext.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Text.Json;
using System.Text.Json.Serialization;
using Azure.Mcp.Tools.Search.Commands.Index;
using Azure.Mcp.Tools.Search.Commands.Knowledge;
@@ -18,7 +19,4 @@ namespace Azure.Mcp.Tools.Search.Commands;
[JsonSerializable(typeof(KnowledgeBaseGetCommand.KnowledgeBaseGetCommandResult))]
[JsonSerializable(typeof(KnowledgeBaseRetrieveCommand.KnowledgeBaseRetrieveCommandResult))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
-internal sealed partial class SearchJsonContext : JsonSerializerContext
-{
- // This class is generated at runtime by the source generator.
-}
+internal sealed partial class SearchJsonContext : JsonSerializerContext;
diff --git a/tools/Azure.Mcp.Tools.Search/src/Commands/Service/ServiceListCommand.cs b/tools/Azure.Mcp.Tools.Search/src/Commands/Service/ServiceListCommand.cs
index dde3f0637e..503320a8c7 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Commands/Service/ServiceListCommand.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Commands/Service/ServiceListCommand.cs
@@ -2,13 +2,12 @@
// Licensed under the MIT License.
using Azure.Mcp.Core.Commands.Subscription;
+using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.Search.Options.Service;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
-using Microsoft.Mcp.Core.Extensions;
using Microsoft.Mcp.Core.Models.Command;
-using Microsoft.Mcp.Core.Models.Option;
namespace Azure.Mcp.Tools.Search.Commands.Service;
@@ -23,33 +22,14 @@ namespace Azure.Mcp.Tools.Search.Commands.Service;
ReadOnly = true,
Secret = false,
LocalRequired = false)]
-public sealed class ServiceListCommand(ILogger logger, ISearchService searchService) : SubscriptionCommand()
+public sealed class ServiceListCommand(ILogger logger, ISearchService searchService, ISubscriptionResolver subscriptionResolver)
+ : SubscriptionCommand(subscriptionResolver)
{
private readonly ILogger _logger = logger;
private readonly ISearchService _searchService = searchService;
- protected override void RegisterOptions(Command command)
+ public override async Task ExecuteAsync(CommandContext context, ServiceListOptions options, CancellationToken cancellationToken)
{
- base.RegisterOptions(command);
- command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsOptional());
- }
-
- protected override ServiceListOptions BindOptions(ParseResult parseResult)
- {
- var options = base.BindOptions(parseResult);
- options.ResourceGroup = parseResult.GetValueOrDefault(OptionDefinitions.Common.ResourceGroup.Name);
- return options;
- }
-
- public override async Task ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
- {
- if (!Validate(parseResult.CommandResult, context.Response).IsValid)
- {
- return context.Response;
- }
-
- var options = BindOptions(parseResult);
-
try
{
var services = await _searchService.ListServices(
@@ -70,5 +50,5 @@ public override async Task ExecuteAsync(CommandContext context,
return context.Response;
}
- internal sealed record ServiceListCommandResult(List Services);
+ public sealed record ServiceListCommandResult(List Services);
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/GlobalUsings.cs b/tools/Azure.Mcp.Tools.Search/src/GlobalUsings.cs
deleted file mode 100644
index 14a017b104..0000000000
--- a/tools/Azure.Mcp.Tools.Search/src/GlobalUsings.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-global using System.CommandLine;
-global using System.Text.Json;
diff --git a/tools/Azure.Mcp.Tools.Search/src/Models/FieldInfo.cs b/tools/Azure.Mcp.Tools.Search/src/Models/FieldInfo.cs
index 894b1d1e46..43e78991be 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Models/FieldInfo.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Models/FieldInfo.cs
@@ -1,16 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
-
namespace Azure.Mcp.Tools.Search.Models;
public sealed record FieldInfo(
- [property: JsonPropertyName("name")] string Name,
- [property: JsonPropertyName("type")] string Type,
- [property: JsonPropertyName("key")] bool? Key,
- [property: JsonPropertyName("searchable")] bool? Searchable,
- [property: JsonPropertyName("filterable")] bool? Filterable,
- [property: JsonPropertyName("sortable")] bool? Sortable,
- [property: JsonPropertyName("facetable")] bool? Facetable,
- [property: JsonPropertyName("retrievable")] bool? Retrievable);
+ string Name,
+ string Type,
+ bool? Key,
+ bool? Searchable,
+ bool? Filterable,
+ bool? Sortable,
+ bool? Facetable,
+ bool? Retrievable);
diff --git a/tools/Azure.Mcp.Tools.Search/src/Models/IndexInfo.cs b/tools/Azure.Mcp.Tools.Search/src/Models/IndexInfo.cs
index f8438ff076..e2f7400e6c 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Models/IndexInfo.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Models/IndexInfo.cs
@@ -1,11 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
-
namespace Azure.Mcp.Tools.Search.Models;
-public sealed record IndexInfo(
- [property: JsonPropertyName("name")] string Name,
- [property: JsonPropertyName("description")] string? Description,
- [property: JsonPropertyName("fields")] List? Fields);
+public sealed record IndexInfo(string Name, string? Description, List? Fields);
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/BaseSearchOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/BaseSearchOptions.cs
deleted file mode 100644
index 8279afd440..0000000000
--- a/tools/Azure.Mcp.Tools.Search/src/Options/BaseSearchOptions.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using System.Text.Json.Serialization;
-using Microsoft.Mcp.Core.Options;
-
-namespace Azure.Mcp.Tools.Search.Options;
-
-public class BaseSearchOptions : GlobalOptions
-{
- [JsonPropertyName(SearchOptionDefinitions.ServiceName)]
- public string? Service { get; set; }
-}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Index/BaseIndexOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Index/BaseIndexOptions.cs
deleted file mode 100644
index 48e484ef15..0000000000
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Index/BaseIndexOptions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using System.Text.Json.Serialization;
-
-namespace Azure.Mcp.Tools.Search.Options.Index;
-
-public class BaseIndexOptions : BaseSearchOptions
-{
- [JsonPropertyName(SearchOptionDefinitions.IndexName)]
- public string? Index { get; set; }
-}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexGetOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexGetOptions.cs
index 2f1198d05b..4cd747c704 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexGetOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexGetOptions.cs
@@ -1,6 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using Microsoft.Mcp.Core.Options;
+
namespace Azure.Mcp.Tools.Search.Options.Index;
-public class IndexGetOptions : BaseIndexOptions;
+public sealed class IndexGetOptions
+{
+ [Option(Description = SearchOptionDescriptions.Service)]
+ public required string Service { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Index)]
+ public string? Index { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
+}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexQueryOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexQueryOptions.cs
index 78bddac29b..b930122402 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexQueryOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Index/IndexQueryOptions.cs
@@ -1,12 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
+using Microsoft.Mcp.Core.Options;
namespace Azure.Mcp.Tools.Search.Options.Index;
-public class IndexQueryOptions : BaseIndexOptions
+public class IndexQueryOptions
{
- [JsonPropertyName(SearchOptionDefinitions.QueryName)]
- public string? Query { get; set; }
+ [Option(Description = "The search query to execute against the Azure AI Search index.")]
+ public required string Query { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Service)]
+ public required string Service { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Index)]
+ public required string Index { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseGetOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseGetOptions.cs
index 1dc2adab47..ae02f95015 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseGetOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseGetOptions.cs
@@ -1,12 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
+using Microsoft.Mcp.Core.Options;
namespace Azure.Mcp.Tools.Search.Options.Knowledge;
-public class KnowledgeBaseGetOptions : BaseSearchOptions
+public sealed class KnowledgeBaseGetOptions
{
- [JsonPropertyName(SearchOptionDefinitions.KnowledgeBaseName)]
+ [Option(Description = SearchOptionDescriptions.KnowledgeBase)]
public string? KnowledgeBase { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Service)]
+ public required string Service { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseRetrieveOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseRetrieveOptions.cs
index b88653e110..3f1ef8c5c9 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseRetrieveOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeBaseRetrieveOptions.cs
@@ -1,18 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
+using Microsoft.Mcp.Core.Options;
namespace Azure.Mcp.Tools.Search.Options.Knowledge;
-public class KnowledgeBaseRetrieveOptions : BaseSearchOptions
+public sealed class KnowledgeBaseRetrieveOptions
{
- [JsonPropertyName(SearchOptionDefinitions.KnowledgeBaseName)]
- public string? KnowledgeBase { get; set; }
+ [Option(Description = SearchOptionDescriptions.KnowledgeBase)]
+ public required string KnowledgeBase { get; set; }
- [JsonPropertyName(SearchOptionDefinitions.QueryName)]
+ [Option(Description = "Natural language query for retrieval when a conversational message history isn't provided.")]
public string? Query { get; set; }
- [JsonPropertyName(SearchOptionDefinitions.MessagesName)]
- public string[] Messages { get; set; } = [];
+ [Option(Description = "Conversation history messages passed to the knowledge base. Able to specify multiple --messages entries. Each entry formatted as role:content, where role is `user` or `assistant` (e.g., user:How many docs?).")]
+ public string[]? Messages { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Service)]
+ public required string Service { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeSourceGetOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeSourceGetOptions.cs
index 1ae21925da..5628897cd9 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeSourceGetOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Knowledge/KnowledgeSourceGetOptions.cs
@@ -1,12 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Text.Json.Serialization;
+using Microsoft.Mcp.Core.Options;
namespace Azure.Mcp.Tools.Search.Options.Knowledge;
-public class KnowledgeSourceGetOptions : BaseSearchOptions
+public sealed class KnowledgeSourceGetOptions
{
- [JsonPropertyName(SearchOptionDefinitions.KnowledgeSourceName)]
+ [Option(Description = "The name of the knowledge source within the Azure AI Search service.")]
public string? KnowledgeSource { get; set; }
+
+ [Option(Description = SearchOptionDescriptions.Service)]
+ public required string Service { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDefinitions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDefinitions.cs
deleted file mode 100644
index f6b0152a91..0000000000
--- a/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDefinitions.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-namespace Azure.Mcp.Tools.Search.Options;
-
-public static class SearchOptionDefinitions
-{
- public const string ServiceName = "service";
- public const string IndexName = "index";
- public const string QueryName = "query";
- public const string KnowledgeBaseName = "knowledge-base";
- public const string KnowledgeSourceName = "knowledge-source";
- public const string MessagesName = "messages";
-
- public static readonly Option Service = new(
- $"--{ServiceName}"
- )
- {
- Description = "The name of the Azure AI Search service (e.g., my-search-service).",
- Required = true
- };
-
- public static readonly Option Index = new(
- $"--{IndexName}"
- )
- {
- Description = "The name of the search index within the Azure AI Search service.",
- Required = true
- };
-
- public static readonly Option Query = new(
- $"--{QueryName}"
- )
- {
- Description = "The search query to execute against the Azure AI Search index.",
- Required = true
- };
-
- public static readonly Option KnowledgeBase = new(
- $"--{KnowledgeBaseName}"
- )
- {
- Description = "The name of the knowledge base within the Azure AI Search service.",
- Required = true
- };
-
- public static readonly Option KnowledgeSource = new(
- $"--{KnowledgeSourceName}"
- )
- {
- Description = "The name of the knowledge source within the Azure AI Search service.",
- Required = true
- };
-
- public static readonly Option KnowledgeQuery = new(
- $"--{QueryName}"
- )
- {
- Description = "Natural language query for retrieval when a conversational message history isn't provided.",
- Required = false
- };
-
- public static readonly Option Messages = new(
- $"--{MessagesName}")
- {
- Description = "Conversation history messages passed to the knowledge base. Able to specify multiple --messages entries. Each entry formatted as role:content, where role is `user` or `assistant` (e.g., user:How many docs?).",
- Arity = ArgumentArity.ZeroOrMore,
- AllowMultipleArgumentsPerToken = true
- };
-}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDescriptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDescriptions.cs
new file mode 100644
index 0000000000..df02540ce5
--- /dev/null
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/SearchOptionDescriptions.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Azure.Mcp.Tools.Search.Options;
+
+internal static class SearchOptionDescriptions
+{
+ internal const string Service = "The name of the Azure AI Search service (e.g., my-search-service).";
+ internal const string Index = "The name of the search index within the Azure AI Search service.";
+ internal const string KnowledgeBase = "The name of the knowledge base within the Azure AI Search service.";
+}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Options/Service/ServiceListOptions.cs b/tools/Azure.Mcp.Tools.Search/src/Options/Service/ServiceListOptions.cs
index 0d0345857a..67411411b2 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Options/Service/ServiceListOptions.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Options/Service/ServiceListOptions.cs
@@ -1,8 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using Azure.Mcp.Core.Options;
using Microsoft.Mcp.Core.Options;
namespace Azure.Mcp.Tools.Search.Options.Service;
-public class ServiceListOptions : SubscriptionOptions;
+public sealed class ServiceListOptions : ISubscriptionOption
+{
+ [Option(Description = OptionDescriptions.ResourceGroup)]
+ public string? ResourceGroup { get; set; }
+
+ [Option(Description = OptionDescriptions.Subscription)]
+ public string? Subscription { get; set; }
+
+ [Option(Description = OptionDescriptions.Tenant)]
+ public string? Tenant { get; set; }
+
+ [OptionContainer(Prefix = "retry")]
+ public RetryPolicyOptions? RetryPolicy { get; set; }
+}
diff --git a/tools/Azure.Mcp.Tools.Search/src/Services/ISearchService.cs b/tools/Azure.Mcp.Tools.Search/src/Services/ISearchService.cs
index e43397d9ba..c26ad84b31 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Services/ISearchService.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Services/ISearchService.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Text.Json;
using Azure.Mcp.Tools.Search.Models;
using Microsoft.Mcp.Core.Options;
diff --git a/tools/Azure.Mcp.Tools.Search/src/Services/SearchService.cs b/tools/Azure.Mcp.Tools.Search/src/Services/SearchService.cs
index f2c1b9242b..f3b330f670 100644
--- a/tools/Azure.Mcp.Tools.Search/src/Services/SearchService.cs
+++ b/tools/Azure.Mcp.Tools.Search/src/Services/SearchService.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System.Text;
+using System.Text.Json;
using System.Text.RegularExpressions;
using Azure.Core.Pipeline;
using Azure.Mcp.Core.Services.Azure;
@@ -16,7 +17,6 @@
using Azure.Search.Documents.KnowledgeBases;
using Azure.Search.Documents.KnowledgeBases.Models;
using Azure.Search.Documents.Models;
-using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Options;
using Microsoft.Mcp.Core.Services.Azure.Authentication;
using Microsoft.Mcp.Core.Services.Caching;
@@ -26,14 +26,12 @@ namespace Azure.Mcp.Tools.Search.Services;
public sealed partial class SearchService(
ISubscriptionService subscriptionService,
ICacheService cacheService,
- ITenantService tenantService,
- ILogger logger)
+ ITenantService tenantService)
: BaseAzureService(tenantService), ISearchService
{
private readonly ITenantService _tenantService = tenantService ?? throw new ArgumentNullException(nameof(tenantService));
private readonly ISubscriptionService _subscriptionService = subscriptionService ?? throw new ArgumentNullException(nameof(subscriptionService));
private readonly ICacheService _cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
- private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
private const string CacheGroup = "search";
private const string SearchServicesCacheKey = "services";
private static readonly TimeSpan s_cacheDurationServices = CacheDurations.ServiceData;
diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/SearchServiceTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/SearchServiceTests.cs
index 5d059c2304..b651c14a9c 100644
--- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/SearchServiceTests.cs
+++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/SearchServiceTests.cs
@@ -7,7 +7,6 @@
using Azure.Mcp.Tools.Search.Services;
using Azure.ResourceManager;
using Azure.Search.Documents.KnowledgeBases.Models;
-using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Options;
using Microsoft.Mcp.Core.Services.Azure.Authentication;
using Microsoft.Mcp.Core.Services.Caching;
@@ -35,11 +34,7 @@ public SearchServiceCacheTests()
cloudConfig.ArmEnvironment.Returns(ArmEnvironment.AzurePublicCloud);
_tenantService.CloudConfiguration.Returns(cloudConfig);
- _service = new SearchService(
- _subscriptionService,
- _cacheService,
- _tenantService,
- Substitute.For>());
+ _service = new SearchService(_subscriptionService, _cacheService, _tenantService);
}
[Fact]
diff --git a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/ServiceListCommandTests.cs b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/ServiceListCommandTests.cs
index 10a523ec75..94ee5834ce 100644
--- a/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/ServiceListCommandTests.cs
+++ b/tools/Azure.Mcp.Tools.Search/tests/Azure.Mcp.Tools.Search.Tests/Service/ServiceListCommandTests.cs
@@ -2,18 +2,18 @@
// Licensed under the MIT License.
using System.Net;
+using Azure.Mcp.Tests.Commands;
using Azure.Mcp.Tools.Search.Commands;
using Azure.Mcp.Tools.Search.Commands.Service;
using Azure.Mcp.Tools.Search.Services;
using Microsoft.Mcp.Core.Options;
-using Microsoft.Mcp.Tests.Client;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using Xunit;
namespace Azure.Mcp.Tools.Search.Tests.Service;
-public class ServiceListCommandTests : CommandUnitTestsBase
+public class ServiceListCommandTests : SubscriptionCommandUnitTestsBase
{
[Fact]
public async Task ExecuteAsync_ReturnsServices_WhenServicesExist()