Skip to content
Open
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
16 changes: 16 additions & 0 deletions .vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"servers": {
"iris-local-mcp": {
"type": "stdio",
"command": "dotnet",
"args": [
"servers/Azure.Mcp.Server/src/bin/Release/net9.0/azmcp.dll",
"server",
"start",
"--namespace",
"iothub"
]
}
},
"inputs": []
}
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"chat.mcp.serverSampling": {
".vscode/mcp.json: iris-local-mcp": {
"allowedDuringChat": true
}
}
Comment thread
Copilot marked this conversation as resolved.
}
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<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" />
<PackageVersion Include="Azure.ResourceManager.IotHub" Version="1.2.0-beta.2" />
<PackageVersion Include="Azure.ResourceManager.CognitiveServices" Version="1.5.1" />
<PackageVersion Include="Azure.ResourceManager.Monitor" Version="1.4.0-beta.4" />
<PackageVersion Include="Azure.ResourceManager.MySql" Version="1.1.2" />
Expand Down
13 changes: 13 additions & 0 deletions dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"azure.mcp": {
"version": "2.0.4",
"commands": [
"azmcp"
],
"rollForward": false
}
}
}
156 changes: 156 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"@azure/mcp": "^3.0.0-beta.19"
}
}
1 change: 1 addition & 0 deletions servers/Azure.Mcp.Server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The Azure MCP Server updates automatically by default whenever a new release com

### Features Added

- Added `azmcp iothub device list` to list device identities in an Azure IoT Hub device registry. The command returns device metadata without authentication keys, applies a default page size of 100 and a maximum of 100 to `--max-count`, returns a validation error when `--max-count` is less than 1, and flags `truncated` (with an explanatory message) when the hub contains more devices than were returned.
- Added Azure IoT Hub integration updates including command docs, e2e prompts, README service coverage, consolidated tool mapping, and CODEOWNERS ownership. [[#3005](https://github.com/microsoft/mcp/pull/3005)]

### Breaking Changes
Expand Down
14 changes: 14 additions & 0 deletions servers/Azure.Mcp.Server/docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2939,6 +2939,20 @@ azmcp loadtesting testrun createorupdate --subscription <subscription> \
azmcp grafana list --subscription <subscription> \
[--resource-group <resource-group>]
```
### Azure IoT Hub Operations

#### Device Registry Operations

```bash
# List devices in an IoT Hub
# Returns one page of device identities. --max-count sets the page size (default 100, maximum 100).
# When the hub has more devices than were returned, the response sets truncated=true with an explanatory message.
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp iothub device list --subscription <subscription> \
--resource-group <resource-group> \
--name <iothub-name> \
[--max-count <max-count>]
```

### Azure Marketplace Operations

Expand Down
2 changes: 2 additions & 0 deletions servers/Azure.Mcp.Server/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ private static async Task<int> Main(string[] args)

services.AddLogging(builder =>
{
// Send console logs to stderr so stdout carries only the command's JSON
// response. Keeps CLI output parseable and avoids corrupting stdio MCP output.
builder.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
builder.SetMinimumLevel(LogLevel.Information);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2128,7 +2128,7 @@
},
{
"name": "get_azure_iot_hub",
"description": "Get details about an Azure IoT Hub.",
"description": "Get details about an Azure IoT Hub and list devices registered in its device registry.",
"toolMetadata": {
"destructive": {
"value": false,
Expand Down Expand Up @@ -2156,7 +2156,8 @@
}
},
"mappedToolList": [
"iothub_hub_get"
"iothub_hub_get",
"iothub_device_list"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions tools/Azure.Mcp.Tools.IoTHub/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Azure.Mcp.Tools.IoTHub.UnitTests")]
[assembly: InternalsVisibleTo("Azure.Mcp.Tools.IoTHub.LiveTests")]
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="Azure.ResourceManager" />
<PackageReference Include="Azure.ResourceManager.IotHub" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="ModelContextProtocol" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.CommandLine;
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.Device;
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.Device;

[CommandMetadata(
Id = "426028ac-ac9b-4348-b549-74c0b53ad3e7",
Name = "list",
Title = "List IoT Hub Devices",
Description = """
List devices in an IoT Hub device registry. Returns device identity metadata without authentication keys.
Use --max-count to limit results (default 100, maximum 100). Values greater than 100 are capped at 100; if more devices exist, truncated=true is set.
Hub names/IDs are case-sensitive and must match exactly.
""",
Destructive = false,
Idempotent = true,
OpenWorld = false,
ReadOnly = true,
Secret = false,
LocalRequired = false)]
public sealed class IoTHubDeviceListCommand(
ILogger<IoTHubDeviceListCommand> logger,
IIoTHubDeviceService service,
ISubscriptionResolver subscriptionResolver)
: SubscriptionCommand<IoTHubDeviceListOptions, DeviceListResult>(subscriptionResolver)
{
private const int DefaultMaxCount = 100;
private const int MinMaxCount = 1;
private const int MaxMaxCount = DefaultMaxCount;

private readonly ILogger<IoTHubDeviceListCommand> _logger = logger;
private readonly IIoTHubDeviceService _service = service;

public override void ValidateOptions(IoTHubDeviceListOptions options, ValidationResult validationResult)
{
base.ValidateOptions(options, validationResult);

if (options.MaxCount is < MinMaxCount)
{
validationResult.Errors.Add(
$"The entered max-count '{options.MaxCount}' is less than 1 device. Please specify a value of at least 1.");
}
}

public override async Task<CommandResponse> ExecuteAsync(
CommandContext context,
IoTHubDeviceListOptions options,
CancellationToken cancellationToken)
{
try
{
var maxCount = options.MaxCount switch
{
null => DefaultMaxCount,
> MaxMaxCount => MaxMaxCount,
_ => options.MaxCount.Value
};

var result = await _service.ListDevices(
options.Name,
options.ResourceGroup,
options.Subscription!,
maxCount,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(result, IoTHubJsonContext.Default.DeviceListResult);

if (result.Truncated)
{
context.Response.Message =
$"Showing the first {maxCount} devices. The hub contains more devices, but the results were truncated because the maximum is {maxCount}.";
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error listing devices in IoT Hub '{HubName}'.", options.Name);
HandleException(context, ex);
}

return context.Response;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.Mcp.Core.Commands.Subscription;
using Azure.Mcp.Core.Services.Azure.Subscription;
using Azure.Mcp.Tools.IoTHub.Models;
Expand Down Expand Up @@ -77,7 +78,9 @@ public override async Task<CommandResponse> ExecuteAsync(
return context.Response;
}

public record IoTHubGetCommandResult(IoTHubDescription IoTHub, bool AreResultsTruncated);
public record IoTHubGetCommandResult(
[property: JsonPropertyName("iotHub")] IoTHubDescription IoTHub,
bool AreResultsTruncated);

private static bool IsValidIoTHubName(string value)
{
Expand Down
Loading
Loading