Skip to content

Commit 97c0fe1

Browse files
authored
Leverage the new IMcpServerInfoService to detect server availability (#7453)
1 parent b67e16e commit 97c0fe1

30 files changed

Lines changed: 492 additions & 189 deletions

Directory.Packages.props

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44

55
<MicrosoftCodeAnalysisPublicApiAnalyzersVersion Condition="'$(MicrosoftCodeAnalysisPublicApiAnalyzersVersion)' == ''">3.3.4</MicrosoftCodeAnalysisPublicApiAnalyzersVersion>
6-
<NewtonsoftJsonPackageVersion Condition="'$(NewtonsoftJsonPackageVersion)' == ''">13.0.3</NewtonsoftJsonPackageVersion>
6+
<NewtonsoftJsonPackageVersion Condition="'$(NewtonsoftJsonPackageVersion)' == ''">13.0.4</NewtonsoftJsonPackageVersion>
77
<SystemFormatsAsn1PackageVersion Condition="'$(SystemFormatsAsn1PackageVersion)' == ''">8.0.2</SystemFormatsAsn1PackageVersion>
88

99
<!-- Read docs/updating-packages.md before updating System.Text.Json's version -->
@@ -55,13 +55,14 @@
5555
<PackageVersion Include="Microsoft.DotNet.SignTool" Version="10.0.0-beta.25367.5" />
5656
<PackageVersion Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
5757
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="$(MicrosoftExtensionsFileSystemGlobbingPackageVersion)" />
58-
<PackageVersion Include="Microsoft.Internal.VisualStudio.Shell.Framework" Version="17.10.40173" />
58+
<PackageVersion Include="Microsoft.Internal.VisualStudio.Shell.Framework" Version="18.8.850" />
5959
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
6060
<PackageVersion Include="Microsoft.PowerShell.3.ReferenceAssemblies" Version="1.0.0" />
6161
<!-- Microsoft.TeamFoundationServer.ExtendedClient has vulnerable dependencies Microsoft.IdentityModel.JsonWebTokens and System.IdentityModel.Tokens.Jwt . When it's upgraded, try removing the pinned packages -->
6262
<PackageVersion Include="Microsoft.TeamFoundationServer.ExtendedClient" Version="16.153.0" />
6363
<PackageVersion Include="Microsoft.Test.Apex.VisualStudio" Version="18.0.0-preview-1-10723-180" />
6464
<PackageVersion Include="Microsoft.TestPlatform.Portable" Version="17.1.0" />
65+
<PackageVersion Include="Microsoft.VisualStudio.ComponentModelHost" Version="18.8.850" />
6566
<PackageVersion Include="Microsoft.VisualStudio.Copilot" Version="18.0.848-alpha" />
6667
<PackageVersion Include="Microsoft.VisualStudio.LanguageServices" Version="4.3.1" />
6768
<PackageVersion Include="Microsoft.VisualStudio.Markdown.Platform" Version="17.14.76-preview" />
@@ -126,7 +127,7 @@
126127
'$(MSBuildProjectFile)' == 'NuGet.VisualStudio.Interop.csproj' OR
127128
'$(MSBuildProjectFile)' == 'NuGet.SolutionRestoreManager.Interop.csproj'">
128129
<PackageVersion Include="Microsoft.ServiceHub.Framework" Version="4.8.55" />
129-
<PackageVersion Include="Microsoft.VisualStudio.ComponentModelHost" Version="17.10.191" />
130+
<PackageVersion Update="Microsoft.VisualStudio.ComponentModelHost" Version="17.10.191" />
130131
<PackageVersion Update="Microsoft.VisualStudio.SDK" Version="" />
131132
<!-- Microsoft.VisualStudio.Shell.15.0 has vulnerable dependencies System.Text.Json. When it's upgraded, try removing the pinned packages -->
132133
<PackageVersion Include="Microsoft.VisualStudio.Shell.15.0" Version="18.0.2345-preview.1" />

NuGet.Config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@
4242
<package pattern="moq" />
4343
<package pattern="MSTest.TestAdapter" />
4444
<package pattern="MSTest.TestFramework" />
45+
<package pattern="Nerdbank.MessagePack" />
4546
<package pattern="nerdbank.streams" />
4647
<package pattern="netstandard.library" />
4748
<package pattern="newtonsoft.json" />
4849
<package pattern="nuget.*" />
50+
<package pattern="Nullable" />
51+
<package pattern="PolyType" />
4952
<package pattern="runtime.*" />
5053
<package pattern="sharpziplib" />
5154
<package pattern="Spectre.*" />

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"tools": {
3-
"dotnet": "10.0.100",
3+
"dotnet": "10.0.300",
44
"pinned": true
55
},
66
"msbuild-sdks": {

src/NuGet.Clients/NuGet.PackageManagement.UI/UserInterfaceService/NuGetUIFactory.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace NuGet.PackageManagement.UI
2525
[Export(typeof(INuGetUIFactory))]
2626
internal sealed class NuGetUIFactory : INuGetUIFactory
2727
{
28-
[Import]
29-
private ICommonOperations CommonOperations { get; set; }
28+
private readonly ICommonOperations _commonOperations;
29+
private readonly INuGetUILogger _outputConsoleLogger;
3030

3131
[Import]
3232
private Lazy<IDeleteOnRestartManager> DeleteOnRestartManager { get; set; }
@@ -37,9 +37,6 @@ internal sealed class NuGetUIFactory : INuGetUIFactory
3737
[Import]
3838
private Lazy<IOptionsPageActivator> OptionsPageActivator { get; set; }
3939

40-
[Import]
41-
private INuGetUILogger OutputConsoleLogger { get; set; }
42-
4340
[Import]
4441
private Lazy<IPackageRestoreManager> PackageRestoreManager { get; set; }
4542

@@ -70,6 +67,8 @@ public NuGetUIFactory(
7067
INuGetUILogger logger,
7168
ISourceControlManagerProvider sourceControlManagerProvider)
7269
{
70+
_commonOperations = commonOperations;
71+
_outputConsoleLogger = logger;
7372
ProjectContext = new NuGetUIProjectContext(
7473
commonOperations,
7574
logger,
@@ -98,7 +97,7 @@ public async ValueTask<INuGetUI> CreateAsync(
9897

9998
return await NuGetUI.CreateAsync(
10099
serviceBroker,
101-
CommonOperations,
100+
_commonOperations,
102101
ProjectContext,
103102
SourceRepositoryProvider.Value,
104103
Settings.Value,
@@ -109,7 +108,7 @@ public async ValueTask<INuGetUI> CreateAsync(
109108
DeleteOnRestartManager.Value,
110109
SolutionUserOptions,
111110
LockService.Value,
112-
OutputConsoleLogger,
111+
_outputConsoleLogger,
113112
RestoreProgressReporter.Value,
114113
NuGetTelemetryProvider,
115114
CancellationToken.None,

src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetSolutionManagerService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public sealed class NuGetSolutionManagerService : INuGetSolutionManagerService
2222
private readonly AuthorizationServiceClient _authorizationServiceClient;
2323

2424
[Import]
25-
private IVsSolutionManager? SolutionManager { get; set; }
25+
private IVsSolutionManager SolutionManager { get; set; } = null!;
2626

2727
public event EventHandler<string>? AfterNuGetCacheUpdated;
2828
public event EventHandler<IProjectContextInfo>? AfterProjectRenamed;

src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Telemetry/FixVulnerabilitiesWithCopilotErrorType.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ public enum FixVulnerabilitiesWithCopilotErrorType
1212
McpToolServiceNotAvailable,
1313
CopilotAccessDenied,
1414
NuGetSolverNotAvailable,
15+
McpServerInfoServiceNotAvailable,
16+
McpServerNotActive,
1517
}
1618
}

src/NuGet.Clients/NuGet.Tools/CopilotToolInvocationService.cs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Threading.Tasks;
1010
using Microsoft.ServiceHub.Framework;
1111
using Microsoft.VisualStudio.Copilot;
12+
using Microsoft.VisualStudio.Copilot.Internal.Mcp;
1213
using Microsoft.VisualStudio.Shell;
1314
using Microsoft.VisualStudio.Shell.ServiceBroker;
1415
using NuGet.VisualStudio;
@@ -22,7 +23,7 @@ internal class CopilotToolInvocationService : ICopilotToolInvocationService
2223
private const string AuthStatusDetermined = "c936efcc-6baa-4ad3-9c2b-7ba750acf18f";
2324
private static readonly Guid CopilotReadyUIContext = new(AuthStatusDetermined);
2425

25-
[Import(typeof(SVsFullAccessServiceBroker))]
26+
[Import(typeof(SVsFullAccessServiceBroker), AllowDefault = true)]
2627
public IServiceBroker? ServiceBroker { get; set; }
2728

2829
public async Task<CopilotToolSessionResult> TryCreateToolSessionAsync(
@@ -46,7 +47,25 @@ public async Task<CopilotToolSessionResult> TryCreateToolSessionAsync(
4647
return CopilotToolSessionResult.Failure(CopilotToolSessionError.ServiceBrokerNotAvailable);
4748
}
4849

49-
// 3. Acquire Copilot service, ownership transfers to CopilotToolSession on success
50+
// 3. Verify the required MCP server is registered and active
51+
IMcpServerInfoService? mcpServerInfoService = await ServiceBroker.GetProxyAsync<IMcpServerInfoService>(McpServiceIdentities.ServerInfoService.Descriptor, cancellationToken);
52+
using (mcpServerInfoService as IDisposable)
53+
{
54+
if (mcpServerInfoService is null)
55+
{
56+
return CopilotToolSessionResult.Failure(CopilotToolSessionError.McpServerInfoServiceNotAvailable);
57+
}
58+
59+
// The NuGet MCP server may be registered under different names depending on how it
60+
// was installed (in-VS vs. Anthropic/GitHub MCP registry). It is considered available
61+
// if any of the acceptable names reports an Active or Suspended state.
62+
if (!await IsServerAvailableAsync(mcpServerInfoService, acceptableMcpServerNames, cancellationToken))
63+
{
64+
return CopilotToolSessionResult.Failure(CopilotToolSessionError.McpServerNotActive);
65+
}
66+
}
67+
68+
// 4. Acquire Copilot service, ownership transfers to CopilotToolSession on success
5069
#pragma warning disable ISB001 // Dispose objects before losing scope - ownership is transferred to CopilotToolSession on success
5170
ICopilotService? copilotService = await ServiceBroker.GetProxyAsync<ICopilotService>(CopilotDescriptors.CopilotService, cancellationToken);
5271
#pragma warning restore ISB001
@@ -59,7 +78,7 @@ public async Task<CopilotToolSessionResult> TryCreateToolSessionAsync(
5978
return CopilotToolSessionResult.Failure(CopilotToolSessionError.CopilotServiceNotAvailable);
6079
}
6180

62-
// 4. Acquire MCP tool function provider and get available functions
81+
// 5. Acquire MCP tool function provider and get available functions
6382
ICopilotFunctionProvider? cfp = await ServiceBroker.GetProxyAsync<ICopilotFunctionProvider>(CopilotDescriptors.McpToolService, cancellationToken);
6483
using (cfp as IDisposable)
6584
{
@@ -68,16 +87,16 @@ public async Task<CopilotToolSessionResult> TryCreateToolSessionAsync(
6887
return CopilotToolSessionResult.Failure(CopilotToolSessionError.McpToolServiceNotAvailable);
6988
}
7089

71-
// 5. Verify the required tool is available. We match on ServerNameOfFunction + Group
90+
// 6. Verify the required tool is available. We match on ServerNameOfFunction + Group
7291
// (the same logical NuGet MCP tool can be exposed under different Group values
73-
// depending on how it was installed — in-VS vs. Anthropic/GitHub MCP registry).
92+
// depending on how it was installed - in-VS vs. Anthropic/GitHub MCP registry).
7493
IReadOnlyList<CopilotFunctionDescriptor>? functions = await cfp.GetFunctionsAsync(correlationId, cancellationToken);
7594
if (!IsToolAvailable(functions, mcpToolName, acceptableMcpServerNames))
7695
{
7796
return CopilotToolSessionResult.Failure(CopilotToolSessionError.ToolNotAvailable);
7897
}
7998

80-
// 6. Start Copilot thread
99+
// 7. Start Copilot thread
81100
CopilotThreadOptions options = new(clientId);
82101
CopilotThread thread = await copilotService.StartThreadAsync(options, cancellationToken);
83102

@@ -109,5 +128,22 @@ internal static bool IsToolAvailable(
109128
&& f.Group is not null
110129
&& acceptableMcpServerNames.Contains(f.Group, StringComparer.OrdinalIgnoreCase)) ?? false;
111130
}
131+
132+
internal static async Task<bool> IsServerAvailableAsync(
133+
IMcpServerInfoService mcpServerInfoService,
134+
IReadOnlyCollection<string> acceptableMcpServerNames,
135+
CancellationToken cancellationToken)
136+
{
137+
foreach (string serverName in acceptableMcpServerNames)
138+
{
139+
McpServerState? state = await mcpServerInfoService.GetServerStateAsync(serverName, cancellationToken);
140+
if (state is McpServerState.Active or McpServerState.Suspended)
141+
{
142+
return true;
143+
}
144+
}
145+
146+
return false;
147+
}
112148
}
113149
}

src/NuGet.Clients/NuGet.Tools/CopilotToolSessionError.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@ internal enum CopilotToolSessionError
1414
CopilotServiceNotAvailable,
1515
McpToolServiceNotAvailable,
1616
ToolNotAvailable,
17+
McpServerInfoServiceNotAvailable,
18+
McpServerNotActive,
1719
}
1820
}

src/NuGet.Clients/NuGet.Tools/FixVulnerablitiesService.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ internal class FixVulnerablitiesService : IFixVulnerabilitiesService
3131
CopilotDefaultTypes.StringName);
3232

3333
[Import(typeof(ICopilotToolInvocationService))]
34-
public ICopilotToolInvocationService? ToolInvocationService { get; set; }
34+
public ICopilotToolInvocationService ToolInvocationService { get; set; } = null!;
3535

36-
[Import(typeof(IVsSolutionManager))]
36+
[Import(typeof(IVsSolutionManager), AllowDefault = true)]
3737
public IVsSolutionManager? SolutionManager { get; set; }
3838

39-
[Import(typeof(VisualStudioActivityLogger))]
39+
[Import(typeof(VisualStudioActivityLogger), AllowDefault = true)]
4040
public ILogger? ActivityLogger { get; set; }
4141

4242
public async Task LaunchFixVulnerabilitiesAsync(CancellationToken cancellationToken)
@@ -93,6 +93,8 @@ private static void HandleSessionError(CopilotToolSessionError error)
9393
CopilotToolSessionError.ServiceBrokerNotAvailable => (FixVulnerabilitiesWithCopilotErrorType.ServiceBrokerNotAvailable, Resources.Error_ServiceBrokerNotAvailable),
9494
CopilotToolSessionError.CopilotServiceNotAvailable => (FixVulnerabilitiesWithCopilotErrorType.CopilotServiceNotAvailable, Resources.Error_CopilotServiceNotAvailable),
9595
CopilotToolSessionError.McpToolServiceNotAvailable => (FixVulnerabilitiesWithCopilotErrorType.McpToolServiceNotAvailable, Resources.Error_McpToolServiceNotAvailable),
96+
CopilotToolSessionError.McpServerInfoServiceNotAvailable => (FixVulnerabilitiesWithCopilotErrorType.McpServerInfoServiceNotAvailable, Resources.Error_McpServerInfoServiceNotAvailable),
97+
CopilotToolSessionError.McpServerNotActive => (FixVulnerabilitiesWithCopilotErrorType.McpServerNotActive, Resources.Error_McpServerNotActive),
9698
CopilotToolSessionError.ToolNotAvailable => (FixVulnerabilitiesWithCopilotErrorType.NuGetSolverNotAvailable, Resources.Error_NuGetSolverNotAvailable),
9799
_ => throw new ArgumentOutOfRangeException(nameof(error), error, null),
98100
};

src/NuGet.Clients/NuGet.Tools/Resources.Designer.cs

Lines changed: 24 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)