Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@
# ServiceLabel: %tools-ISV
# ServiceOwners: @agrimayadav @pachaturvedi

# PRLabel: %tools-IoTHub
/tools/Azure.Mcp.Tools.IoTHub/ @rrao @microsoft/azure-mcp

# ServiceLabel: %tools-IoTHub
# ServiceOwners: @rrao @microsoft/azure-mcp

# PRLabel: %tools-KeyVault
/tools/Azure.Mcp.Tools.KeyVault/ @JonathanCrd @vcolin7 @microsoft/azure-mcp

Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<PackageVersion Include="Azure.ResourceManager.HDInsight" Version="1.2.0-beta.5" />
<PackageVersion Include="Azure.ResourceManager.DesktopVirtualization" Version="1.3.2" />
<PackageVersion Include="Azure.ResourceManager.DeviceRegistry" Version="1.1.0-beta.2" />
<PackageVersion Include="Azure.ResourceManager.IotHub" Version="1.1.1" />
Comment thread
jichang1 marked this conversation as resolved.
Outdated
<PackageVersion Include="Azure.ResourceManager.ContainerService" Version="1.2.5" />
<PackageVersion Include="Azure.ResourceManager.EventGrid" Version="1.2.0-beta.2" />
<PackageVersion Include="Azure.ResourceManager.EventHubs" Version="1.2.1" />
Expand Down
8 changes: 8 additions & 0 deletions Microsoft.Mcp.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@
<Folder Name="/tools/Azure.Mcp.Tools.Grafana/tests/">
<Project Path="tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.Tests/Azure.Mcp.Tools.Grafana.Tests.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/" />
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/src/">
<Project Path="tools/Azure.Mcp.Tools.IoTHub/src/Azure.Mcp.Tools.IoTHub.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/tests/">
<Project Path="tools/Azure.Mcp.Tools.IoTHub/tests/Azure.Mcp.Tools.IoTHub.LiveTests/Azure.Mcp.Tools.IoTHub.LiveTests.csproj" />
<Project Path="tools/Azure.Mcp.Tools.IoTHub/tests/Azure.Mcp.Tools.IoTHub.UnitTests/Azure.Mcp.Tools.IoTHub.UnitTests.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.KeyVault/" />
<Folder Name="/tools/Azure.Mcp.Tools.KeyVault/src/">
<Project Path="tools/Azure.Mcp.Tools.KeyVault/src/Azure.Mcp.Tools.KeyVault.csproj" />
Expand Down
8 changes: 8 additions & 0 deletions servers/Azure.Mcp.Server/Azure.Mcp.Server.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@
<Folder Name="/tools/Azure.Mcp.Tools.Grafana/tests/">
<Project Path="../../tools/Azure.Mcp.Tools.Grafana/tests/Azure.Mcp.Tools.Grafana.Tests/Azure.Mcp.Tools.Grafana.Tests.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/" />
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/src/">
<Project Path="../../tools/Azure.Mcp.Tools.IoTHub/src/Azure.Mcp.Tools.IoTHub.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.IoTHub/tests/">
<Project Path="../../tools/Azure.Mcp.Tools.IoTHub/tests/Azure.Mcp.Tools.IoTHub.LiveTests/Azure.Mcp.Tools.IoTHub.LiveTests.csproj" />
<Project Path="../../tools/Azure.Mcp.Tools.IoTHub/tests/Azure.Mcp.Tools.IoTHub.UnitTests/Azure.Mcp.Tools.IoTHub.UnitTests.csproj" />
</Folder>
<Folder Name="/tools/Azure.Mcp.Tools.KeyVault/" />
<Folder Name="/tools/Azure.Mcp.Tools.KeyVault/src/">
<Project Path="../../tools/Azure.Mcp.Tools.KeyVault/src/Azure.Mcp.Tools.KeyVault.csproj" />
Expand Down
7 changes: 7 additions & 0 deletions servers/Azure.Mcp.Server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,12 @@ Example prompts that generate Azure CLI commands:
* "Get provisioning recommendations for my file share workload"
* "Get usage data and metrics for my file share"

### 🌐 Azure IoT Hub

* "List all IoT Hubs in my subscription"
* "Show me IoT Hubs in resource group 'my-resource-group'"
* "Get details for IoT Hub 'my-iot-hub' in resource group 'my-resource-group'"

### 🔑 Azure Key Vault

* "List all secrets in my key vault 'my-vault'"
Expand Down Expand Up @@ -1251,6 +1257,7 @@ The Azure MCP Server provides tools for interacting with **44+ Azure service are
- 📊 **Azure Event Grid** - Event routing and management
- 📁 **Azure File Shares** - Azure managed file share operations
- ⚡ **Azure Functions** - Function App management and functions project files, language support, and templates source code
- 🌐 **Azure IoT Hub** - IoT Hub resource discovery and details
- 🔑 **Azure Key Vault** - Secrets, keys, and certificates
- ☸️ **Azure Kubernetes Service (AKS)** - Container orchestration
- 📦 **Azure Load Testing** - Performance testing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changes:
- section: "Features Added"
description: "Added Azure IoT Hub integration updates including command docs, e2e prompts, README service coverage, consolidated tool mapping, and CODEOWNERS ownership."
10 changes: 10 additions & 0 deletions servers/Azure.Mcp.Server/docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2714,6 +2714,16 @@ azmcp functions template get --language <language> \
[--runtime-version <runtime-version>]
```

### Azure IoT Hub Operations

```bash
# Get IoT Hub details or list IoT Hubs in a subscription or resource group
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp iothub hub get --subscription <subscription> \
[--resource-group <resource-group>] \
[--name <iot-hub-name>]
```

### Azure Key Vault Operations

#### Administration
Expand Down
8 changes: 8 additions & 0 deletions servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@ This file contains prompts used for end-to-end testing to ensure each tool is in
| functions_template_get | Show me a Cosmos DB trigger with an output binding in Java |
| functions_template_get | Generate a MCP Tool trigger in TypeScript for Node.js 22 |

## Azure IoT Hub

| Tool Name | Test Prompt |
|:----------|:----------|
| iothub_hub_get | List all IoT Hubs in my subscription |
| iothub_hub_get | Show me all IoT Hubs in resource group <resource_group_name> |
| iothub_hub_get | Get details for IoT Hub <name> in resource group <resource_group_name> |

## Azure Key Vault

| Tool Name | Test Prompt |
Expand Down
1 change: 1 addition & 0 deletions servers/Azure.Mcp.Server/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ private static IAreaSetup[] RegisterAreas()
new Azure.Mcp.Tools.FunctionApp.FunctionAppSetup(),
new Azure.Mcp.Tools.Functions.FunctionsSetup(),
new Azure.Mcp.Tools.Grafana.GrafanaSetup(),
new Azure.Mcp.Tools.IoTHub.IoTHubSetup(),
new Azure.Mcp.Tools.KeyVault.KeyVaultSetup(),
new Azure.Mcp.Tools.Kusto.KustoSetup(),
new Azure.Mcp.Tools.LoadTesting.LoadTestingSetup(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2087,7 +2087,7 @@
},
{
"name": "get_azure_messaging_service_details",
"description": "Get details about Azure messaging services including Service Bus queues, topics, subscriptions, Event Grid topics and subscriptions, and Event Hubs namespaces, event hubs, consumer groups, and streaming event ingestion resources.",
"description": "Get details about Azure messaging services including Service Bus queues, topics, subscriptions, Event Grid topics and subscriptions, Event Hubs namespaces, event hubs, consumer groups, IoT Hubs, and streaming event ingestion resources.",
"toolMetadata": {
"destructive": {
"value": false,
Expand Down Expand Up @@ -2122,7 +2122,8 @@
"servicebus_topic_subscription_details",
"eventhubs_namespace_get",
"eventhubs_eventhub_get",
"eventhubs_eventhub_consumergroup_get"
"eventhubs_eventhub_consumergroup_get",
"iothub_hub_get"
]
},
{
Expand Down
7 changes: 7 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Azure.Mcp.Tools.IoTHub.UnitTests")]
[assembly: InternalsVisibleTo("Azure.Mcp.Tools.IoTHub.LiveTests")]
20 changes: 20 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/Azure.Mcp.Tools.IoTHub.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="**\Resources\*.txt" />
<EmbeddedResource Include="**\Resources\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Azure.Mcp.Core\src\Azure.Mcp.Core.csproj" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Azure.ResourceManager.IotHub" />
Comment thread
jichang1 marked this conversation as resolved.
Outdated
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="ModelContextProtocol" />
<PackageReference Include="System.CommandLine" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.IoTHub.Models;
using Azure.Mcp.Tools.IoTHub.Options.IoTHub;
using Azure.Mcp.Tools.IoTHub.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Models.Command;

namespace Azure.Mcp.Tools.IoTHub.Commands.IoTHub;

[CommandMetadata(
Id = "108daa19-5c1b-4633-9a56-d58f4da020e1",
Name = "get",
Title = "Get IoT Hub",
Description = """
Get IoT Hub details or list IoT Hubs in a subscription. Optionally filter by resource group and IoT Hub name.
Returns each IoT Hub with id, name, location, resourceGroup, subscriptionId, sku, capacity, state, and hostName.
""",
Destructive = false,
Idempotent = true,
OpenWorld = false,
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class IoTHubGetCommand(
ILogger<IoTHubGetCommand> logger,
IIoTHubService service,
ISubscriptionResolver subscriptionResolver)
: SubscriptionCommand<IoTHubGetOptions, IoTHubGetCommand.IoTHubGetCommandResult>(subscriptionResolver)
{
private readonly ILogger<IoTHubGetCommand> _logger = logger;
private readonly IIoTHubService _service = service;

public override async Task<CommandResponse> ExecuteAsync(
CommandContext context,
IoTHubGetOptions options,
CancellationToken cancellationToken)
{
try
{
var results = await _service.GetIoTHub(
options.Name,
options.ResourceGroup,
options.Subscription!,
options.Tenant,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(
new(results?.Results ?? [], results?.AreResultsTruncated ?? false),
IoTHubJsonContext.Default.IoTHubGetCommandResult);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error getting IoT Hub(s). Subscription: {Subscription}, ResourceGroup: {ResourceGroup}, Name: {Name}.",
options.Subscription,
options.ResourceGroup,
options.Name);
HandleException(context, ex);
}

return context.Response;
}

public record IoTHubGetCommandResult(List<IoTHubDescription> IoTHubs, bool AreResultsTruncated);
}
17 changes: 17 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/Commands/IoTHubJsonContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.Mcp.Tools.IoTHub.Commands.IoTHub;
using Azure.Mcp.Tools.IoTHub.Models;

namespace Azure.Mcp.Tools.IoTHub.Commands;

[JsonSerializable(typeof(IoTHubGetCommand.IoTHubGetCommandResult))]
[JsonSerializable(typeof(IoTHubDescription))]
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
internal sealed partial class IoTHubJsonContext : JsonSerializerContext
{
}
7 changes: 7 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

global using System;
global using System.Collections.Generic;
global using System.Threading;
global using System.Threading.Tasks;
37 changes: 37 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/IoTHubSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Tools.IoTHub.Commands.IoTHub;
using Azure.Mcp.Tools.IoTHub.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Mcp.Core.Areas;
using Microsoft.Mcp.Core.Commands;

namespace Azure.Mcp.Tools.IoTHub;

public class IoTHubSetup : IAreaSetup
{
public string Name => "iothub";

public string Title => "Manage Azure IoT Hub";

public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IIoTHubService, IoTHubService>();
services.AddSingleton<IoTHubGetCommand>();
}

public CommandGroup RegisterCommands(IServiceProvider serviceProvider)
{
var iothub = new CommandGroup(Name,
"IoT Hub operations - Commands for managing Azure IoT Hubs.",
Title);

var hub = new CommandGroup("hub", "IoT Hub resource operations.");
iothub.AddSubGroup(hub);

hub.AddCommand<IoTHubGetCommand>(serviceProvider);

return iothub;
}
}
15 changes: 15 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/Models/IoTHubDescription.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Azure.Mcp.Tools.IoTHub.Models;

public record IoTHubDescription(
string Id,
string Name,
string Location,
string ResourceGroup,
string SubscriptionId,
string Sku,
long Capacity,
string State,
string HostName);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Options;
using Microsoft.Mcp.Core.Options;

namespace Azure.Mcp.Tools.IoTHub.Options.IoTHub;

public class IoTHubGetOptions : ISubscriptionOption
{
[Option(Description = "The name of the IoT Hub.")]
public string? Name { get; set; }

[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; }
}
19 changes: 19 additions & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/Services/IIoTHubService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Tools.IoTHub.Models;
using Azure.Mcp.Core.Services.Azure;
using Microsoft.Mcp.Core.Options;

namespace Azure.Mcp.Tools.IoTHub.Services;

public interface IIoTHubService
{
Task<ResourceQueryResults<IoTHubDescription>> GetIoTHub(
string? name,
string? resourceGroup,
string subscription,
string? tenant = null,
RetryPolicyOptions? retryPolicy = null,
CancellationToken cancellationToken = default);
}
Loading
Loading