Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c7b0df6
Using ARG query to list & get Sql data
ArthurMa1978 Aug 11, 2025
a6f267e
update
ArthurMa1978 Aug 12, 2025
6348e68
update
ArthurMa1978 Aug 12, 2025
762f37d
update doc
ArthurMa1978 Aug 12, 2025
d856d23
update
ArthurMa1978 Aug 12, 2025
d57e70d
update
ArthurMa1978 Aug 12, 2025
9e1d572
update
ArthurMa1978 Aug 12, 2025
5aa0562
update
ArthurMa1978 Aug 13, 2025
eca7dd4
update
ArthurMa1978 Aug 13, 2025
f6856c6
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 13, 2025
57dd806
update
ArthurMa1978 Aug 13, 2025
a39d6bc
update
ArthurMa1978 Aug 13, 2025
e4e324a
update
ArthurMa1978 Aug 14, 2025
c6b4574
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 14, 2025
fe8ed38
update
ArthurMa1978 Aug 14, 2025
e588fad
revert vscode/README.md
ArthurMa1978 Aug 14, 2025
228ffc0
udpate
ArthurMa1978 Aug 14, 2025
a7c3c06
update
ArthurMa1978 Aug 15, 2025
b7d3879
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 15, 2025
0661eac
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 15, 2025
e391dab
update changelog
ArthurMa1978 Aug 15, 2025
84acd0b
update changelog
ArthurMa1978 Aug 15, 2025
54986bf
update checklist
ArthurMa1978 Aug 15, 2025
04b5116
Update the identify unused properteis check
ArthurMa1978 Aug 15, 2025
caecb2e
update
ArthurMa1978 Aug 15, 2025
631c25e
fix typo
ArthurMa1978 Aug 15, 2025
7f1cc08
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 18, 2025
9a5418e
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 19, 2025
3bdb737
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 19, 2025
4bd863c
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 20, 2025
67818a9
Merge branch 'main' into mgmt-sql
ArthurMa1978 Aug 20, 2025
c985dbe
update changelog
ArthurMa1978 Aug 20, 2025
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
2 changes: 2 additions & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"Groq",
"HKCU",
"HKEY_CURRENT_USER",
"Hyperscale",
"Intune",
"LASTEXITCODE",
"LPUTF8Str",
Expand Down Expand Up @@ -110,6 +111,7 @@
"contentfiles",
"credscan",
"cslschema",
"cutover",
"datatable",
"datistemplate",
"datname",
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ The Azure MCP Server updates automatically by default whenever a new release com

### Features Added

- Introduced `BaseAzureResourceService` class to perform Azure Resource read operations using Azure Resource Graph queries. [[#938](https://github.com/Azure/azure-mcp/pull/938)]

### Breaking Changes

### Bugs Fixed

- Fixed SQL service test assertions to use case-insensitive string comparisons for resource type validation. [[#938](https://github.com/Azure/azure-mcp/pull/938)]
- Fixed HttpClient service test assertions to properly validate NoProxy collection handling instead of expecting a single string value. [[#938](https://github.com/Azure/azure-mcp/pull/938)]

### Other Changes

- Refactored SQL service implementation to use Azure Resource Graph queries instead of direct ARM API calls. [[#938](https://github.com/Azure/azure-mcp/pull/938)]
- Removed dependency on `Azure.ResourceManager.Sql` package by migrating to Azure Resource Graph queries, reducing package size and improving startup performance.
- Enhanced `BaseAzureService` with `EscapeKqlString` method for safe KQL query construction across all Azure services. [[#938](https://github.com/Azure/azure-mcp/pull/938)]
- Fixed KQL string escaping in Workbooks service queries.

## 0.5.7 (2025-08-19)

### Features Added
Expand Down
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
<PackageVersion Include="Azure.ResourceManager.RedisEnterprise" Version="1.2.1" />
<PackageVersion Include="Azure.ResourceManager.ResourceHealth" Version="1.0.0" />
<PackageVersion Include="Azure.ResourceManager.LoadTesting" Version="1.1.2" />
<PackageVersion Include="Azure.ResourceManager.Sql" Version="1.4.0-beta.3" />
<PackageVersion Include="Azure.ResourceManager.Network" Version="1.11.2" />
<PackageVersion Include="Azure.ResourceManager.MachineLearning" Version="1.2.3" />
<PackageVersion Include="Azure.Security.KeyVault.Keys" Version="4.7.0" />
Expand Down
1 change: 0 additions & 1 deletion areas/sql/src/AzureMcp.Sql/AzureMcp.Sql.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
<ItemGroup />
<ItemGroup>
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Azure.ResourceManager.Sql" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="System.CommandLine" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,
options.Subscription!,
options.RetryPolicy);

if (database == null)
{
context.Response.Status = 404;
context.Response.Message = $"Database '{options.Database}' not found on server '{options.Server}' in resource group '{options.ResourceGroup}'.";
return context.Response;
}

context.Response.Results = ResponseResult.Create(
new DatabaseShowResult(database),
SqlJsonContext.Default.DatabaseShowResult);
Expand All @@ -77,6 +70,7 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,

protected override string GetErrorMessage(Exception ex) => ex switch
{
KeyNotFoundException => $"SQL database not found. Verify the database name, server name, resource group, and that you have access.",
Azure.RequestFailedException reqEx when reqEx.Status == 404 =>
"Database or server not found. Verify the database name, server name, resource group, and that you have access.",
Azure.RequestFailedException reqEx when reqEx.Status == 403 =>
Expand All @@ -87,6 +81,7 @@ public override async Task<CommandResponse> ExecuteAsync(CommandContext context,

protected override int GetStatusCode(Exception ex) => ex switch
{
KeyNotFoundException => 404,
Azure.RequestFailedException reqEx => reqEx.Status,
_ => base.GetStatusCode(ex)
};
Expand Down
8 changes: 8 additions & 0 deletions areas/sql/src/AzureMcp.Sql/Commands/SqlJsonContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using AzureMcp.Sql.Commands.EntraAdmin;
using AzureMcp.Sql.Commands.FirewallRule;
using AzureMcp.Sql.Models;
using AzureMcp.Sql.Services.Models;

namespace AzureMcp.Sql.Commands;

Expand All @@ -22,6 +23,13 @@ namespace AzureMcp.Sql.Commands;
[JsonSerializable(typeof(DatabaseSku))]
[JsonSerializable(typeof(ElasticPoolSku))]
[JsonSerializable(typeof(ElasticPoolPerDatabaseSettings))]
[JsonSerializable(typeof(SqlDatabaseData))]
[JsonSerializable(typeof(SqlDatabaseProperties))]
[JsonSerializable(typeof(SqlServerAadAdministratorData))]
[JsonSerializable(typeof(SqlElasticPoolData))]
[JsonSerializable(typeof(SqlElasticPoolProperties))]
[JsonSerializable(typeof(SqlElasticPoolPerDatabaseSettings))]
[JsonSerializable(typeof(SqlFirewallRuleData))]
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = true,
Expand Down
5 changes: 3 additions & 2 deletions areas/sql/src/AzureMcp.Sql/Services/ISqlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ public interface ISqlService
/// <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 database information, or null if not found</returns>
Task<SqlDatabase?> GetDatabaseAsync(
/// <returns>The SQL database information</returns>
/// <exception cref="KeyNotFoundException">Thrown when the database is not found</exception>
Task<SqlDatabase> GetDatabaseAsync(
string serverName,
string databaseName,
string resourceGroup,
Expand Down
38 changes: 38 additions & 0 deletions areas/sql/src/AzureMcp.Sql/Services/Models/SqlDatabaseData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using AzureMcp.Sql.Commands;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlDatabase data model.
/// A database resource.
/// </summary>
internal sealed class SqlDatabaseData
{
/// <summary> The resource ID for the resource. </summary>
[JsonPropertyName("id")]
public string? ResourceId { get; set; }
/// <summary> The type of the resource. </summary>
[JsonPropertyName("type")]
public string? ResourceType { get; set; }
/// <summary> The name of the resource. </summary>
[JsonPropertyName("name")]
public string? ResourceName { get; set; }
/// <summary> The location of the resource. </summary>
public string? Location { get; set; }
/// <summary> The database SKU. </summary>
public SqlSku? Sku { get; set; }
/// <summary> Properties of the Sql database. </summary>
public SqlDatabaseProperties? Properties { get; set; }

// Read the JSON response content and create a model instance from it.
public static SqlDatabaseData? FromJson(JsonElement source)
{
return JsonSerializer.Deserialize<SqlDatabaseData>(source, SqlJsonContext.Default.SqlDatabaseData);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Text.Json.Serialization;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlDatabase properties model.
/// A database resource properties.
/// </summary>
internal sealed class SqlDatabaseProperties
{
/// <summary> The collation of the database. </summary>
public string? Collation { get; set; }
/// <summary> The max size of the database expressed in bytes. </summary>
public long? MaxSizeBytes { get; set; }
/// <summary> The resource identifier of the elastic pool containing this database. </summary>
public string? ElasticPoolId { get; set; }
/// <summary> The status of the database. </summary>
public string? Status { get; set; }
/// <summary> The creation date of the database (ISO8601 format). </summary>
[JsonPropertyName("creationDate")]
public DateTimeOffset? CreatedOn { get; set; }
/// <summary> The current service level objective name of the database. </summary>
public string? CurrentServiceObjectiveName { get; set; }
/// <summary> The license type to apply for this database. `LicenseIncluded` if you need a license, or `BasePrice` if you have a license and are eligible for the Azure Hybrid Benefit. </summary>
public string? LicenseType { get; set; }
/// <summary> This records the earliest start date and time that restore is available for this database (ISO8601 format). </summary>
[JsonPropertyName("earliestRestoreDate")]
public DateTimeOffset? EarliestRestoreOn { get; set; }
/// <summary> The state of read-only routing. If enabled, connections that have application intent set to readonly in their connection string may be routed to a readonly secondary replica in the same region. Not applicable to a Hyperscale database within an elastic pool. </summary>
public string? ReadScale { get; set; }
/// <summary> Whether or not this database is zone redundant, which means the replicas of this database will be spread across multiple availability zones. </summary>
public bool? IsZoneRedundant { get; set; }
/// <summary> The name and tier of the SKU. </summary>
public SqlSku? CurrentSku { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using AzureMcp.Sql.Commands;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the ElasticPool data model.
/// An elastic pool.
/// </summary>
internal sealed class SqlElasticPoolData
{
/// <summary> The resource ID for the resource. </summary>
[JsonPropertyName("id")]
public string? ResourceId { get; set; }
/// <summary> The type of the resource. </summary>
[JsonPropertyName("type")]
public string? ResourceType { get; set; }
/// <summary> The name of the resource. </summary>
[JsonPropertyName("name")]
public string? ResourceName { get; set; }
/// <summary> The location of the resource. </summary>
public string? Location { get; set; }
/// <summary> The database SKU. </summary>
public SqlSku? Sku { get; set; }
/// <summary> The properties of elastic pool. </summary>
public SqlElasticPoolProperties? Properties { get; set; }

// Read the JSON response content and create a model instance from it.
public static SqlElasticPoolData? FromJson(JsonElement source)
{
return JsonSerializer.Deserialize<SqlElasticPoolData>(source, SqlJsonContext.Default.SqlElasticPoolData);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace AzureMcp.Sql.Services.Models
{
/// <summary> Per database settings of an elastic pool. </summary>
internal sealed class SqlElasticPoolPerDatabaseSettings
{
/// <summary> The minimum capacity all databases are guaranteed. </summary>
public double? MinCapacity { get; set; }
/// <summary> The maximum capacity any one database can consume. </summary>
public double? MaxCapacity { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the ElasticPool properties model.
/// An elastic pool properties.
/// </summary>
internal sealed class SqlElasticPoolProperties
{
/// <summary> The state of the elastic pool. </summary>
public string? State { get; set; }
/// <summary> The creation date of the elastic pool (ISO8601 format). </summary>
[JsonPropertyName("creationDate")]
public DateTimeOffset? CreatedOn { get; set; }
/// <summary> The storage limit for the database elastic pool in bytes. </summary>
public long? MaxSizeBytes { get; set; }
/// <summary> The per database settings for the elastic pool. </summary>
public SqlElasticPoolPerDatabaseSettings? PerDatabaseSettings { get; set; }
/// <summary> Whether or not this elastic pool is zone redundant, which means the replicas of this elastic pool will be spread across multiple availability zones. </summary>
[JsonPropertyName("zoneRedundant")]
public bool? IsZoneRedundant { get; set; }
/// <summary> The license type to apply for this elastic pool. </summary>
public string? LicenseType { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using AzureMcp.Sql.Commands;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlFirewallRule data model.
/// A server firewall rule.
/// </summary>
internal sealed class SqlFirewallRuleData
{
/// <summary> The resource ID for the resource. </summary>
[JsonPropertyName("id")]
public string? ResourceId { get; set; }
/// <summary> The type of the resource. </summary>
[JsonPropertyName("type")]
public string? ResourceType { get; set; }
/// <summary> The name of the resource. </summary>
[JsonPropertyName("name")]
public string? ResourceName { get; set; }
/// <summary> The properties of the firewall rule. </summary>
public SqlFirewallRuleProperties? Properties { get; set; }

// Read the JSON response content and create a model instance from it.
public static SqlFirewallRuleData? FromJson(JsonElement source)
{
return JsonSerializer.Deserialize<SqlFirewallRuleData>(source, SqlJsonContext.Default.SqlFirewallRuleData);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlFirewallRule properties data model.
/// A server firewall rule properties.
/// </summary>
internal sealed class SqlFirewallRuleProperties
{
/// <summary> The start IP address of the firewall rule. Must be IPv4 format. Use value '0.0.0.0' for all Azure-internal IP addresses. </summary>
[JsonPropertyName("startIpAddress")]
public string? StartIPAddress { get; set; }
/// <summary> The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value '0.0.0.0' for all Azure-internal IP addresses. </summary>
[JsonPropertyName("endIpAddress")]
public string? EndIPAddress { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json;
using System.Text.Json.Serialization;
using AzureMcp.Sql.Commands;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlServerAadAdministrator data model.
/// Azure Active Directory administrator.
/// </summary>
internal sealed class SqlServerAadAdministratorData
{
/// <summary> The resource ID for the resource. </summary>
[JsonPropertyName("id")]
public string? ResourceId { get; set; }
/// <summary> The type of the resource. </summary>
[JsonPropertyName("type")]
public string? ResourceType { get; set; }
/// <summary> The name of the resource. </summary>
[JsonPropertyName("name")]
public string? ResourceName { get; set; }
/// <summary> Properties of the Azure Active Directory administrator. </summary>
public SqlServerAadAdministratorProperties? Properties { get; set; }

// Read the JSON response content and create a model instance from it.
public static SqlServerAadAdministratorData? FromJson(JsonElement source)
{
return JsonSerializer.Deserialize<SqlServerAadAdministratorData>(source, SqlJsonContext.Default.SqlServerAadAdministratorData);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json.Serialization;

namespace AzureMcp.Sql.Services.Models
{
/// <summary>
/// A class representing the SqlServerAadAdministrator properties model.
/// Azure Active Directory administrator properties.
/// </summary>
internal sealed class SqlServerAadAdministratorProperties
{
/// <summary> Type of the sever administrator. </summary>
public string? AdministratorType { get; set; }
/// <summary> Login name of the server administrator. </summary>
public string? Login { get; set; }
/// <summary> SID (object ID) of the server administrator. </summary>
public Guid? Sid { get; set; }
/// <summary> Tenant ID of the administrator. </summary>
public Guid? TenantId { get; set; }
/// <summary> Azure Active Directory only Authentication enabled. </summary>
[JsonPropertyName("azureADOnlyAuthentication")]
public bool? IsAzureADOnlyAuthenticationEnabled { get; set; }
}
}
Loading
Loading