Skip to content

DI Refactor: Azure.Mcp.Tools.Extension#1988

Merged
conniey merged 6 commits intomainfrom
copilot/refactor-azure-mcp-tools-extension
Mar 11, 2026
Merged

DI Refactor: Azure.Mcp.Tools.Extension#1988
conniey merged 6 commits intomainfrom
copilot/refactor-azure-mcp-tools-extension

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 10, 2026

  • Refactor AzCommand.cs: inject IExternalProcessService via constructor
  • Refactor AzqrCommand.cs: inject ISubscriptionService, IDateTimeProvider, IExternalProcessService via constructor
  • Refactor CliInstallCommand.cs: inject ICliInstallService via constructor
  • Refactor CliGenerateCommand.cs: inject ICliGenerateService via constructor
  • Update ExtensionSetup.cs: revert out-of-scope services.AddSingleton<AzCommand>() registration
  • Update AzqrCommandTests.cs: pass injected services to constructor
  • Update CliInstallCommandTests.cs: pass injected service to constructor
  • Update CliGenerateCommandTests.cs: pass injected service to constructor
  • Update ExtensionSetupTests.cs: add mock service registrations for setup tests
  • Fix VisualStudioToolNameTests.cs: add missing mock registrations for IDateTimeProvider, IExternalProcessService, IAzureTokenCredentialProvider, IAzureCloudConfiguration
  • Fix CommandFactoryHelpers.cs: add missing mock registrations for IDateTimeProvider, IExternalProcessService, IAzureTokenCredentialProvider, IAzureCloudConfiguration
  • Build and test verified (22/22 Extension tests pass, 822/822 Azure.Mcp.Core.UnitTests pass)
  • Add changelog entry (servers/Azure.Mcp.Server/changelog-entries/copilot-di-refactor-extension.yaml) with PR number 1988

Invoking Livetests

Copilot submitted PRs are not trustworthy by default. Users with write access to the repo need to validate the contents of this PR before leaving a comment with the text /azp run mcp - pullrequest - live. This will trigger the necessary livetest workflows to complete required validation.

Original prompt

This section details on the original issue you should resolve

<issue_title>DI Refactor: Azure.Mcp.Tools.Extension</issue_title>
<issue_description>## Summary

Refactor Azure.Mcp.Tools.Extension to use constructor dependency injection instead of resolving services via context.GetService<T>() in ExecuteAsync.

This is a sub-issue of #158.

Instructions

Follow the algorithm described in tools/di-refactor-plan.md with {ToolArea} = Extension.

An example PR is: #1815
</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…sion commands

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor Azure.Mcp.Tools.Extension for constructor dependency injection DI Refactor: Azure.Mcp.Tools.Extension Mar 10, 2026
Comment thread tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs
Comment thread tools/Azure.Mcp.Tools.Extension/src/ExtensionSetup.cs Outdated
…1988)

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 10, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh pr list --repo microsoft/mcp --head copilot/refactor-azure-mcp-tools-extension (http block)
  • i1qvsblobprodcus353.vsblob.vsassets.io
    • Triggering command: /usr/bin/pwsh pwsh -Command ./eng/scripts/Compile-Changelog.ps1 -ChangelogPath &#39;servers/Azure.Mcp.Server/CHANGELOG.md&#39; -DryRun (dns block)
  • www.powershellgallery.com
    • Triggering command: /usr/bin/pwsh pwsh -Command ./eng/scripts/Compile-Changelog.ps1 -ChangelogPath &#39;servers/Azure.Mcp.Server/CHANGELOG.md&#39; -DryRun (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

…tup.cs

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
conniey
conniey previously approved these changes Mar 10, 2026
@conniey conniey marked this pull request as ready for review March 10, 2026 17:42
@conniey conniey requested review from a team, jongio and wbreza as code owners March 10, 2026 17:42
conniey
conniey previously approved these changes Mar 10, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Azure.Mcp.Tools.Extension to use constructor dependency injection instead of resolving services via context.GetService<T>() inside ExecuteAsync methods. This is part of a larger effort (#158) to improve testability and make dependencies explicit.

Changes:

  • All four command classes (AzCommand, AzqrCommand, CliInstallCommand, CliGenerateCommand) now receive their required services via constructor injection instead of pulling them lazily from CommandContext
  • ExtensionSetupTests.cs adds mock registrations for the newly required dependencies so the test service provider can construct the refactored commands
  • Unit tests for AzqrCommand, CliInstallCommand, and CliGenerateCommand are updated to pass mocked services directly to command constructors

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Commands/AzCommand.cs Injects IExternalProcessService via constructor, removes context.GetService call
src/Commands/AzqrCommand.cs Injects ISubscriptionService, IDateTimeProvider, IExternalProcessService via constructor, removes three context.GetService calls
src/Commands/CliInstallCommand.cs Injects ICliInstallService via constructor, removes context.GetService call
src/Commands/CliGenerateCommand.cs Injects ICliGenerateService via constructor, removes context.GetService call
tests/.../ExtensionSetupTests.cs Adds mocks for IExternalProcessService, ISubscriptionService, IDateTimeProvider, IAzureTokenCredentialProvider, IAzureCloudConfiguration needed to build the service provider
tests/.../CliInstallCommandTests.cs Passes _cliInstallService directly to CliInstallCommand constructor
tests/.../CliGenerateCommandTests.cs Passes _cliGenerateService directly to CliGenerateCommand constructor
tests/.../AzqrCommandTests.cs Passes mocked services directly to AzqrCommand constructor
changelog-entries/copilot-di-refactor-extension.yaml Adds changelog entry for this refactoring

Comment thread tools/Azure.Mcp.Tools.Extension/src/Commands/AzCommand.cs
@conniey
Copy link
Copy Markdown
Member

conniey commented Mar 10, 2026

@copilot Fix this unit test failure:

[xUnit.net 00:00:01.31]   Starting:    Azure.Mcp.Tools.ServiceBus.UnitTests
  Failed Azure.Mcp.Server.UnitTests.Infrastructure.VisualStudioToolNameTests.AllMode_VisualStudioToolNames_MustNotChange [194 ms]
  Error Message:
   System.InvalidOperationException : Unable to resolve service for type 'Azure.Mcp.Core.Services.Time.IDateTimeProvider' while attempting to activate 'Azure.Mcp.Tools.Extension.Commands.AzqrCommand'.
  Stack Trace:
     at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Azure.Mcp.Tools.Extension.ExtensionSetup.RegisterCommands(IServiceProvider serviceProvider) in D:\a\_work\1\s\tools\Azure.Mcp.Tools.Extension\src\ExtensionSetup.cs:line 44
   at Azure.Mcp.Core.Commands.CommandFactory.RegisterCommandGroup() in D:\a\_work\1\s\core\Microsoft.Mcp.Core\src\Commands\CommandFactory.cs:line 142
   at Azure.Mcp.Core.Commands.CommandFactory.CreateRootCommand() in D:\a\_work\1\s\core\Microsoft.Mcp.Core\src\Commands\CommandFactory.cs:line 250
   at Azure.Mcp.Core.Commands.CommandFactory..ctor(IServiceProvider serviceProvider, IEnumerable`1 serviceAreas, ITelemetryService telemetryService, IOptions`1 configurationOptions, ILogger`1 logger) in D:\a\_work\1\s\core\Microsoft.Mcp.Core\src\Commands\CommandFactory.cs:line 67
   at Azure.Mcp.Server.UnitTests.Infrastructure.VisualStudioToolNameTests.GetAllModeToolNamesAsync() in D:\a\_work\1\s\servers\Azure.Mcp.Server\tests\Azure.Mcp.Server.UnitTests\Infrastructure\VisualStudioToolNameTests.cs:line 62
   at Azure.Mcp.Server.UnitTests.Infrastructure.VisualStudioToolNameTests.AllMode_VisualStudioToolNames_MustNotChange() in D:\a\_work\1\s\servers\Azure.Mcp.Server\tests\Azure.Mcp.Server.UnitTests\Infrastructure\VisualStudioToolNameTests.cs:line 78
--- End of stack trace from previous location ---

…alProcessService, IAzureTokenCredentialProvider, IAzureCloudConfiguration mocks

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 10, 2026

@copilot Fix this unit test failure:

...

Fixed in commit 47ddaab. VisualStudioToolNameTests now registers mocks for IDateTimeProvider, IExternalProcessService, IAzureTokenCredentialProvider, and IAzureCloudConfiguration — the server-level services that AzqrCommand and CliGenerateService require after the DI refactor.

@conniey
Copy link
Copy Markdown
Member

conniey commented Mar 11, 2026

@copilot check that the tests in Azure.Mcp.Core.UnitTests are passing. They are currently failing. An example:

[xUnit.net 00:00:03.18]     Azure.Mcp.Core.UnitTests.Areas.Server.Commands.Discovery.CommandGroupDiscoveryStrategyTests.DiscoverServersAsync_ProvidersAreCommandGroupServerProviderType [FAIL]
[xUnit.net 00:00:03.18]       System.InvalidOperationException : Unable to resolve service for type 'Azure.Mcp.Core.Services.Time.IDateTimeProvider' while attempting to activate 'Azure.Mcp.Tools.Extension.Commands.AzqrCommand'.
[xUnit.net 00:00:03.18]       Stack Trace:
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, ServiceIdentifier serviceIdentifier, Type implementationType, CallSiteChain callSiteChain)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, Int32 slot)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
[xUnit.net 00:00:03.18]            at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
[xUnit.net 00:00:03.18]            at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/tools/Azure.Mcp.Tools.Extension/src/ExtensionSetup.cs(44,0): at Azure.Mcp.Tools.Extension.ExtensionSetup.RegisterCommands(IServiceProvider serviceProvider)
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Microsoft.Mcp.Core/src/Commands/CommandFactory.cs(142,0): at Azure.Mcp.Core.Commands.CommandFactory.RegisterCommandGroup()
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Microsoft.Mcp.Core/src/Commands/CommandFactory.cs(250,0): at Azure.Mcp.Core.Commands.CommandFactory.CreateRootCommand()
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Microsoft.Mcp.Core/src/Commands/CommandFactory.cs(67,0): at Azure.Mcp.Core.Commands.CommandFactory..ctor(IServiceProvider serviceProvider, IEnumerable`1 serviceAreas, ITelemetryService telemetryService, IOptions`1 configurationOptions, ILogger`1 logger)
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs(113,0): at Azure.Mcp.Core.UnitTests.Areas.Server.CommandFactoryHelpers.CreateCommandFactory(IServiceProvider serviceProvider)
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs(19,0): at Azure.Mcp.Core.UnitTests.Areas.Server.Commands.Discovery.CommandGroupDiscoveryStrategyTests.CreateStrategy(ICommandFactory commandFactory, ServiceStartOptions options, String entryPoint)
[xUnit.net 00:00:03.18]         /mnt/vss/_work/1/s/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/CommandGroupDiscoveryStrategyTests.cs(314,0): at Azure.Mcp.Core.UnitTests.Areas.Server.Commands.Discovery.CommandGroupDiscoveryStrategyTests.DiscoverServersAsync_ProvidersAreCommandGroupServerProviderType()
[xUnit.net 00:00:03.18]         --- End of stack trace from previous location ---

…lProcessService, IAzureTokenCredentialProvider, IAzureCloudConfiguration mocks to CommandFactoryHelpers

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 11, 2026

@copilot check that the tests in Azure.Mcp.Core.UnitTests are passing. They are currently failing. An example:

[xUnit.net 00:00:03.18]     Azure.Mcp.Core.UnitTests.Areas.Server...

Fixed in commit 6834567. Added mocks for IDateTimeProvider, IExternalProcessService, IAzureTokenCredentialProvider, and IAzureCloudConfiguration to CommandFactoryHelpers.SetupCommonServices() — the shared helper used by all Azure.Mcp.Core.UnitTests that build a CommandFactory with the full set of area setups including ExtensionSetup.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • learn.microsoft.com
    • Triggering command: /home/REDACTED/work/mcp/mcp/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/bin/Debug/net10.0/Azure.Mcp.Core.UnitTests /home/REDACTED/work/mcp/mcp/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/bin/Debug/net10.0/Azure.Mcp.Core.UnitTests @@ /tmp/tmpB5ovh0.tmp (dns block)
    • Triggering command: /home/REDACTED/work/mcp/mcp/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/bin/Debug/net10.0/Azure.Mcp.Core.UnitTests /home/REDACTED/work/mcp/mcp/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/bin/Debug/net10.0/Azure.Mcp.Core.UnitTests @@ /tmp/tmpLzxenm.tmp /Debug/net10.0/Azure.Mcp.Core.UnitTests (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@github-actions
Copy link
Copy Markdown

Hi @copilot. We've noticed that new changes have been pushed to this pull request. Because it is set to automatically merge, we've reset the approvals to allow the opportunity to review the updates.

@github-actions github-actions bot dismissed stale reviews from conniey and conniey March 11, 2026 07:23

Hi @conniey. We've noticed that new changes have been pushed to this pull request. Because it is set to automatically merge, we've reset the approvals to allow the opportunity to review the updates.

@github-project-automation github-project-automation bot moved this from Untriaged to In Progress in Azure MCP Server Mar 11, 2026
@conniey conniey merged commit 8df0c5d into main Mar 11, 2026
15 checks passed
@conniey conniey deleted the copilot/refactor-azure-mcp-tools-extension branch March 11, 2026 16:46
@github-project-automation github-project-automation bot moved this from In Progress to Done in Azure MCP Server Mar 11, 2026
colbytimm pushed a commit to colbytimm/microsoft-mcp that referenced this pull request Apr 20, 2026
* Initial plan

* DI refactor: inject services via constructor in Azure.Mcp.Tools.Extension commands

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>

* Add changelog entry for DI refactor of Azure.Mcp.Tools.Extension (PR microsoft#1988)

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>

* Revert out-of-scope AzCommand singleton registration from ExtensionSetup.cs

Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: conniey <10136526+conniey@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

DI Refactor: Azure.Mcp.Tools.Extension

4 participants