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
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Net;
using Azure.Mcp.Core.Commands;
using Azure.Mcp.Tools.Sql.Models;
using Azure.Mcp.Tools.Sql.Options.Server;
using Azure.Mcp.Tools.Sql.Services;
using Microsoft.Extensions.Logging;

namespace Azure.Mcp.Tools.Sql.Commands.Server;

public sealed class ServerConnPolicyShowCommand(ILogger<ServerConnPolicyShowCommand> logger)
: BaseSqlCommand<ServerConnPolicyShowOptions>(logger)
{
private const string CommandTitle = "Show SQL Server Connection Policy";

public override string Name => "show";

public override string Description =>
"""
Retrieves the connection policy for an Azure SQL Server. The connection policy determines
how clients connect to the SQL server and can be one of: Default (uses Azure defaults),
Proxy (all connections are proxied through Azure gateway), or Redirect (connections are
redirected directly to the database node).
""";

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 sqlService = context.GetService<ISqlService>();

var connectionPolicy = await sqlService.GetServerConnectionPolicyAsync(
options.Server!,
options.ResourceGroup!,
options.Subscription!,
options.RetryPolicy);

context.Response.Results = ResponseResult.Create(new(connectionPolicy), SqlJsonContext.Default.ServerConnPolicyShowResult);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error retrieving SQL server connection policy. Server: {Server}, ResourceGroup: {ResourceGroup}, Options: {@Options}",
options.Server, options.ResourceGroup, options);
HandleException(context, ex);
}

return context.Response;
}

protected override string GetErrorMessage(Exception ex) => ex switch
{
KeyNotFoundException =>
"SQL server connection policy not found. Verify the server name and resource group.",
RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.NotFound =>
"SQL server or connection policy not found. Verify the server name and resource group.",
RequestFailedException reqEx when reqEx.Status == (int)HttpStatusCode.Forbidden =>
$"Authorization failed retrieving the SQL server connection policy. Verify you have appropriate permissions. Details: {reqEx.Message}",
RequestFailedException reqEx => reqEx.Message,
ArgumentException argEx => $"Invalid parameter: {argEx.Message}",
_ => base.GetErrorMessage(ex)
};

protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch
{
KeyNotFoundException => HttpStatusCode.NotFound,
RequestFailedException reqEx => (HttpStatusCode)reqEx.Status,
ArgumentException => HttpStatusCode.BadRequest,
_ => base.GetStatusCode(ex)
};

internal record ServerConnPolicyShowResult(SqlServerConnectionPolicy ConnectionPolicy);
}
2 changes: 2 additions & 0 deletions tools/Azure.Mcp.Tools.Sql/src/Commands/SqlJsonContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ namespace Azure.Mcp.Tools.Sql.Commands;
[JsonSerializable(typeof(ServerDeleteCommand.ServerDeleteResult))]
[JsonSerializable(typeof(ServerListCommand.ServerListResult))]
[JsonSerializable(typeof(ServerShowCommand.ServerShowResult))]
[JsonSerializable(typeof(ServerConnPolicyShowCommand.ServerConnPolicyShowResult))]
[JsonSerializable(typeof(ElasticPoolListCommand.ElasticPoolListResult))]
[JsonSerializable(typeof(SqlDatabase))]
[JsonSerializable(typeof(SqlServer))]
[JsonSerializable(typeof(SqlServerConnectionPolicy))]
[JsonSerializable(typeof(SqlServerEntraAdministrator))]
[JsonSerializable(typeof(SqlServerFirewallRule))]
[JsonSerializable(typeof(SqlElasticPool))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.Mcp.Tools.Sql.Models;

public record SqlServerConnectionPolicy(
string Name,
string Id,
string Type,
string ConnectionType);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.Mcp.Tools.Sql.Options.Server;

public class ServerConnPolicyShowOptions : BaseSqlOptions
{
}
16 changes: 16 additions & 0 deletions tools/Azure.Mcp.Tools.Sql/src/Services/ISqlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,20 @@ Task<bool> DeleteServerAsync(
string subscription,
RetryPolicyOptions? retryPolicy,
CancellationToken cancellationToken = default);

/// <summary>
/// Gets the connection policy for a SQL server.
/// </summary>
/// <param name="serverName">The name of the SQL server</param>
/// <param name="resourceGroup">The name of the resource group</param>
/// <param name="subscription">The subscription ID or name</param>
/// <param name="retryPolicy">Optional retry policy options</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>The SQL server connection policy information</returns>
Task<SqlServerConnectionPolicy> GetServerConnectionPolicyAsync(
string serverName,
string resourceGroup,
string subscription,
RetryPolicyOptions? retryPolicy,
CancellationToken cancellationToken = default);
}
64 changes: 64 additions & 0 deletions tools/Azure.Mcp.Tools.Sql/src/Services/SqlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,4 +1109,68 @@ private static SqlServerFirewallRule ConvertToSqlFirewallRuleModel(JsonElement i
EndIpAddress: firewallRule.Properties?.EndIPAddress
);
}

private static SqlServerConnectionPolicy ConvertToSqlServerConnectionPolicyModel(JsonElement item)
{
var nameValue = item.GetProperty("name").GetString() ?? "Unknown";
var idValue = item.GetProperty("id").GetString() ?? "Unknown";
var typeValue = item.GetProperty("type").GetString() ?? "Unknown";
var connectionTypeValue = "Default";

if (item.TryGetProperty("properties", out var properties))
{
if (properties.TryGetProperty("connectionType", out var connectionType))
{
connectionTypeValue = connectionType.GetString() ?? "Default";
}
}

return new SqlServerConnectionPolicy(
Name: nameValue,
Id: idValue,
Type: typeValue,
ConnectionType: connectionTypeValue);
}

/// <summary>
/// Retrieves the connection policy for an Azure SQL Server.
/// The connection policy determines how clients connect to the SQL server (Default, Proxy, or Redirect).
/// </summary>
/// <param name="serverName">The name of the SQL server to get the connection policy for</param>
/// <param name="resourceGroup">The name of the resource group containing the server</param>
/// <param name="subscription">The subscription ID or name</param>
/// <param name="retryPolicy">Optional retry policy configuration for resilient operations</param>
/// <param name="cancellationToken">Token to observe for cancellation requests</param>
/// <returns>The SQL server connection policy information</returns>
/// <exception cref="ArgumentException">Thrown when required parameters are null or empty</exception>
public async Task<SqlServerConnectionPolicy> GetServerConnectionPolicyAsync(
string serverName,
string resourceGroup,
string subscription,
RetryPolicyOptions? retryPolicy,
CancellationToken cancellationToken = default)
{
ValidateRequiredParameters(
(nameof(serverName), serverName),
(nameof(resourceGroup), resourceGroup),
(nameof(subscription), subscription)
);

// Use Resource Graph to query connection policy
var result = await ExecuteSingleResourceQueryAsync(
"Microsoft.Sql/servers/connectionPolicies",
resourceGroup: resourceGroup,
subscription: subscription,
retryPolicy: retryPolicy,
converter: ConvertToSqlServerConnectionPolicyModel,
additionalFilter: $"name =~ '{EscapeKqlString(serverName)}/default'",
cancellationToken: cancellationToken);

if (result == null)
{
throw new KeyNotFoundException($"Connection policy not found for SQL server '{serverName}' in resource group '{resourceGroup}'.");
}

return result;
}
}
7 changes: 7 additions & 0 deletions tools/Azure.Mcp.Tools.Sql/src/SqlSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<ServerDeleteCommand>();
services.AddSingleton<ServerListCommand>();
services.AddSingleton<ServerShowCommand>();
services.AddSingleton<ServerConnPolicyShowCommand>();

services.AddSingleton<ElasticPoolListCommand>();

Expand Down Expand Up @@ -74,6 +75,12 @@ public CommandGroup RegisterCommands(IServiceProvider serviceProvider)
var serverShow = serviceProvider.GetRequiredService<ServerShowCommand>();
server.AddCommand(serverShow.Name, serverShow);

var connPolicy = new CommandGroup("conn-policy", "SQL server connection policy operations");
server.AddSubGroup(connPolicy);

var connPolicyShow = serviceProvider.GetRequiredService<ServerConnPolicyShowCommand>();
connPolicy.AddCommand(connPolicyShow.Name, connPolicyShow);

var elasticPool = new CommandGroup("elastic-pool", "SQL elastic pool operations");
sql.AddSubGroup(elasticPool);
var elasticPoolList = serviceProvider.GetRequiredService<ElasticPoolListCommand>();
Expand Down
Loading
Loading