From c9e7df6e58dccdfb3defa01087e574aa346b73d1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Feb 2026 22:20:21 +0000
Subject: [PATCH 1/5] Initial plan
From 01d34241c3417d7d12efe544610a8476782589dd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Feb 2026 22:36:07 +0000
Subject: [PATCH 2/5] Update MCP packages to 1.0.0 and fix breaking API changes
- Update ModelContextProtocol and ModelContextProtocol.AspNetCore from 0.8.0-preview.1 to 1.0.0
- Update Microsoft.Extensions.* transitive dependencies from 10.0.2 to 10.0.3
- Migrate AddListToolsFilter/AddCallToolFilter to WithRequestFilters API
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
---
Directory.Packages.props | 4 ++--
eng/Versions.props | 14 ++++++------
src/Aspire.Dashboard/Mcp/McpExtensions.cs | 27 ++++++++++++-----------
3 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 03cc81a3c29..5b3f472aafa 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -110,8 +110,8 @@
-
-
+
+
diff --git a/eng/Versions.props b/eng/Versions.props
index 00baed1bab2..1f58062f866 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -82,18 +82,18 @@
10.0.2
10.0.2
- 10.0.2
+ 10.0.3
10.0.2
10.0.2
- 10.0.2
+ 10.0.3
10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
10.0.2
10.0.2
- 10.0.2
+ 10.0.3
diff --git a/src/Aspire.Dashboard/Mcp/McpExtensions.cs b/src/Aspire.Dashboard/Mcp/McpExtensions.cs
index 6c2dd726ea7..a307bf91b70 100644
--- a/src/Aspire.Dashboard/Mcp/McpExtensions.cs
+++ b/src/Aspire.Dashboard/Mcp/McpExtensions.cs
@@ -48,19 +48,20 @@ public static IMcpServerBuilder AddAspireMcpTools(this IServiceCollection servic
}
builder
- .AddListToolsFilter((next) => async (RequestContext request, CancellationToken cancellationToken) =>
- {
- // Calls here are via the tools/list endpoint. See https://modelcontextprotocol.info/docs/concepts/tools/
- // There is no tool name so we hardcode name to list_tools here so we can reuse the same event.
- //
- // We want to track when users list tools as it's an indicator of whether Aspire MCP is configured (client tools refresh tools via it).
- // It's called even if no Aspire tools end up being used.
- return await RecordCallToolNameAsync(next, request, "list_tools", cancellationToken).ConfigureAwait(false);
- })
- .AddCallToolFilter((next) => async (RequestContext request, CancellationToken cancellationToken) =>
- {
- return await RecordCallToolNameAsync(next, request, request.Params?.Name, cancellationToken).ConfigureAwait(false);
- });
+ .WithRequestFilters(filters => filters
+ .AddListToolsFilter((next) => async (RequestContext request, CancellationToken cancellationToken) =>
+ {
+ // Calls here are via the tools/list endpoint. See https://modelcontextprotocol.info/docs/concepts/tools/
+ // There is no tool name so we hardcode name to list_tools here so we can reuse the same event.
+ //
+ // We want to track when users list tools as it's an indicator of whether Aspire MCP is configured (client tools refresh tools via it).
+ // It's called even if no Aspire tools end up being used.
+ return await RecordCallToolNameAsync(next, request, "list_tools", cancellationToken).ConfigureAwait(false);
+ })
+ .AddCallToolFilter((next) => async (RequestContext request, CancellationToken cancellationToken) =>
+ {
+ return await RecordCallToolNameAsync(next, request, request.Params?.Name, cancellationToken).ConfigureAwait(false);
+ }));
return builder;
}
From 68f3b2fd66e4bfbc5d13974ace1971b1ff63256c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Feb 2026 22:45:36 +0000
Subject: [PATCH 3/5] Fix MCP integration tests for protocol changes in 1.0.0
MCP 1.0.0 requires an initialize handshake before other requests.
Updated tests to properly initialize the MCP session first.
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
---
.../Integration/McpServiceTests.cs | 122 ++++++++++++++++--
.../Integration/StartupTests.cs | 3 +-
2 files changed, 116 insertions(+), 9 deletions(-)
diff --git a/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs b/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
index 16a9a0b7e65..34beaccdcc7 100644
--- a/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
+++ b/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
@@ -32,7 +32,8 @@ public async Task CallService_McpEndPoint_Success()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var request = CreateListToolsRequest();
+ var sessionId = await InitializeSessionAsync(httpClient);
+ var request = CreateListToolsRequest(sessionId);
// Act
var responseMessage = await httpClient.SendAsync(request).DefaultTimeout(TestConstants.LongTimeoutDuration);
@@ -105,11 +106,42 @@ public async Task CallService_McpEndPoint_RequiredApiKeySent_Success()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var requestMessage = CreateListToolsRequest();
+ var requestMessage = CreateInitializeRequest();
requestMessage.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
// Act
- var responseMessage = await httpClient.SendAsync(requestMessage).DefaultTimeout(TestConstants.LongTimeoutDuration);
+ var initResponse = await httpClient.SendAsync(requestMessage).DefaultTimeout(TestConstants.LongTimeoutDuration);
+ initResponse.EnsureSuccessStatusCode();
+ var sessionId = initResponse.Headers.GetValues("Mcp-Session-Id").First();
+
+ // Consume the SSE response body to properly release the connection
+ await initResponse.Content.ReadAsStringAsync().DefaultTimeout(TestConstants.LongTimeoutDuration);
+
+ // Send initialized notification
+ var notificationJson =
+ """
+ {
+ "jsonrpc": "2.0",
+ "method": "notifications/initialized"
+ }
+ """;
+ var notificationContent = new ByteArrayContent(Encoding.UTF8.GetBytes(notificationJson));
+ notificationContent.Headers.TryAddWithoutValidation("content-type", "application/json");
+ var notificationRequest = new HttpRequestMessage(HttpMethod.Post, "/mcp")
+ {
+ Content = notificationContent
+ };
+ notificationRequest.Headers.TryAddWithoutValidation("accept", "application/json");
+ notificationRequest.Headers.TryAddWithoutValidation("accept", "text/event-stream");
+ notificationRequest.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
+ notificationRequest.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
+ var notificationResponse = await httpClient.SendAsync(notificationRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
+ notificationResponse.EnsureSuccessStatusCode();
+
+ var listRequest = CreateListToolsRequest(sessionId);
+ listRequest.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
+
+ var responseMessage = await httpClient.SendAsync(listRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
responseMessage.EnsureSuccessStatusCode();
var responseData = await GetDataFromSseResponseAsync(responseMessage);
@@ -130,7 +162,8 @@ public async Task CallService_NoResourceService_ResourceToolsNotRegistered()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var request = CreateListToolsRequest();
+ var sessionId = await InitializeSessionAsync(httpClient);
+ var request = CreateListToolsRequest(sessionId);
// Act
var responseMessage = await httpClient.SendAsync(request).DefaultTimeout(TestConstants.LongTimeoutDuration);
@@ -166,7 +199,8 @@ public async Task CallService_WithResourceService_ResourceToolsRegistered()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var request = CreateListToolsRequest();
+ var sessionId = await InitializeSessionAsync(httpClient);
+ var request = CreateListToolsRequest(sessionId);
// Act
var responseMessage = await httpClient.SendAsync(request).DefaultTimeout(TestConstants.LongTimeoutDuration);
@@ -226,7 +260,8 @@ public async Task CallService_McpEndPointHttpOnly_Success()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var request = CreateListToolsRequest();
+ var sessionId = await InitializeSessionAsync(httpClient);
+ var request = CreateListToolsRequest(sessionId);
// Act
var responseMessage = await httpClient.SendAsync(request).DefaultTimeout(TestConstants.LongTimeoutDuration);
@@ -263,7 +298,8 @@ public async Task CallService_McpTool_TelemetryRecorded()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var request = CreateListToolsRequest();
+ var sessionId = await InitializeSessionAsync(httpClient);
+ var request = CreateListToolsRequest(sessionId);
// Act
var responseMessage = await httpClient.SendAsync(request).DefaultTimeout(TestConstants.LongTimeoutDuration);
@@ -297,7 +333,73 @@ public async Task CallService_McpTool_TelemetryRecorded()
Assert.True(foundEndOperation, "Expected to find EndOperation telemetry event");
}
- internal static HttpRequestMessage CreateListToolsRequest()
+ internal static HttpRequestMessage CreateInitializeRequest(string? sessionId = null)
+ {
+ var json =
+ """
+ {
+ "jsonrpc": "2.0",
+ "id": "init",
+ "method": "initialize",
+ "params": {
+ "protocolVersion": "2025-03-26",
+ "capabilities": {},
+ "clientInfo": {
+ "name": "test-client",
+ "version": "1.0.0"
+ }
+ }
+ }
+ """;
+ var content = new ByteArrayContent(Encoding.UTF8.GetBytes(json));
+ content.Headers.TryAddWithoutValidation("content-type", "application/json");
+ var request = new HttpRequestMessage(HttpMethod.Post, "/mcp")
+ {
+ Content = content
+ };
+ request.Headers.TryAddWithoutValidation("accept", "application/json");
+ request.Headers.TryAddWithoutValidation("accept", "text/event-stream");
+ if (sessionId is not null)
+ {
+ request.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
+ }
+ return request;
+ }
+
+ internal static async Task InitializeSessionAsync(HttpClient httpClient)
+ {
+ var initRequest = CreateInitializeRequest();
+ var initResponse = await httpClient.SendAsync(initRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
+ initResponse.EnsureSuccessStatusCode();
+ var sessionId = initResponse.Headers.GetValues("Mcp-Session-Id").First();
+
+ // Consume the SSE response body to properly release the connection
+ await initResponse.Content.ReadAsStringAsync().DefaultTimeout(TestConstants.LongTimeoutDuration);
+
+ // Send initialized notification
+ var notificationJson =
+ """
+ {
+ "jsonrpc": "2.0",
+ "method": "notifications/initialized"
+ }
+ """;
+ var notificationContent = new ByteArrayContent(Encoding.UTF8.GetBytes(notificationJson));
+ notificationContent.Headers.TryAddWithoutValidation("content-type", "application/json");
+ var notificationRequest = new HttpRequestMessage(HttpMethod.Post, "/mcp")
+ {
+ Content = notificationContent
+ };
+ notificationRequest.Headers.TryAddWithoutValidation("accept", "application/json");
+ notificationRequest.Headers.TryAddWithoutValidation("accept", "text/event-stream");
+ notificationRequest.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
+ var notificationResponse = await httpClient.SendAsync(notificationRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
+ notificationResponse.EnsureSuccessStatusCode();
+
+ return sessionId;
+ }
+
+ internal static HttpRequestMessage CreateListToolsRequest(string? sessionId = null)
{
var json =
"""
@@ -316,6 +418,10 @@ internal static HttpRequestMessage CreateListToolsRequest()
};
request.Headers.TryAddWithoutValidation("accept", "application/json");
request.Headers.TryAddWithoutValidation("accept", "text/event-stream");
+ if (sessionId is not null)
+ {
+ request.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
+ }
return request;
}
diff --git a/tests/Aspire.Dashboard.Tests/Integration/StartupTests.cs b/tests/Aspire.Dashboard.Tests/Integration/StartupTests.cs
index 51ac994d4af..c0473235abe 100644
--- a/tests/Aspire.Dashboard.Tests/Integration/StartupTests.cs
+++ b/tests/Aspire.Dashboard.Tests/Integration/StartupTests.cs
@@ -416,7 +416,8 @@ await ServerRetryHelper.BindPortWithRetry(async port =>
{
BaseAddress = new Uri($"https://{app.McpEndPointAccessor().EndPoint}")
};
- var mcpRequest = McpServiceTests.CreateListToolsRequest();
+ var mcpSessionId = await McpServiceTests.InitializeSessionAsync(mcpHttpClient);
+ var mcpRequest = McpServiceTests.CreateListToolsRequest(mcpSessionId);
var responseMessage = await mcpHttpClient.SendAsync(mcpRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
responseMessage.EnsureSuccessStatusCode();
From caaa39516ab3454eaf0860d7e05b5264deb2f0c1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Feb 2026 22:57:39 +0000
Subject: [PATCH 4/5] Update all .NET 10.0 package versions from 10.0.2 to
10.0.3
Updates EF Core, ASP.NET Core, Runtime, and other .NET 10.0 packages
in eng/Versions.props from 10.0.2 to 10.0.3.
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
---
eng/Versions.props | 42 +++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/eng/Versions.props b/eng/Versions.props
index 1f58062f866..c763b0d9ba4 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -39,7 +39,7 @@
11.0.0-beta.25610.3
11.0.0-beta.25610.3
11.0.0-beta.25610.3
- 10.0.2
+ 10.0.3
10.2.0
10.1.0-preview.1.25608.1
@@ -65,34 +65,34 @@
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
- 10.0.2
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
+ 10.0.3
10.0.3
- 10.0.2
- 10.0.2
+ 10.0.3
+ 10.0.3
10.0.3
- 10.0.2
+ 10.0.3
10.0.3
10.0.3
10.0.3
10.0.3
- 10.0.2
- 10.0.2
+ 10.0.3
+ 10.0.3
10.0.3
From f4b1c7c1a27dc710e4e90b34c56ea3eed5953da9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 27 Feb 2026 23:29:12 +0000
Subject: [PATCH 5/5] Refactor RequiredApiKeySent test to use
InitializeSessionAsync
Add optional configureRequest callback to InitializeSessionAsync and
use it in CallService_McpEndPoint_RequiredApiKeySent_Success to reduce
code duplication.
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
---
.../Integration/McpServiceTests.cs | 38 +++----------------
1 file changed, 6 insertions(+), 32 deletions(-)
diff --git a/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs b/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
index 34beaccdcc7..04bff74f41e 100644
--- a/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
+++ b/tests/Aspire.Dashboard.Tests/Integration/McpServiceTests.cs
@@ -106,40 +106,12 @@ public async Task CallService_McpEndPoint_RequiredApiKeySent_Success()
using var httpClient = IntegrationTestHelpers.CreateHttpClient($"http://{app.McpEndPointAccessor().EndPoint}");
- var requestMessage = CreateInitializeRequest();
- requestMessage.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
+ void AddApiKey(HttpRequestMessage r) => r.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
- // Act
- var initResponse = await httpClient.SendAsync(requestMessage).DefaultTimeout(TestConstants.LongTimeoutDuration);
- initResponse.EnsureSuccessStatusCode();
- var sessionId = initResponse.Headers.GetValues("Mcp-Session-Id").First();
-
- // Consume the SSE response body to properly release the connection
- await initResponse.Content.ReadAsStringAsync().DefaultTimeout(TestConstants.LongTimeoutDuration);
-
- // Send initialized notification
- var notificationJson =
- """
- {
- "jsonrpc": "2.0",
- "method": "notifications/initialized"
- }
- """;
- var notificationContent = new ByteArrayContent(Encoding.UTF8.GetBytes(notificationJson));
- notificationContent.Headers.TryAddWithoutValidation("content-type", "application/json");
- var notificationRequest = new HttpRequestMessage(HttpMethod.Post, "/mcp")
- {
- Content = notificationContent
- };
- notificationRequest.Headers.TryAddWithoutValidation("accept", "application/json");
- notificationRequest.Headers.TryAddWithoutValidation("accept", "text/event-stream");
- notificationRequest.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
- notificationRequest.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
- var notificationResponse = await httpClient.SendAsync(notificationRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
- notificationResponse.EnsureSuccessStatusCode();
+ var sessionId = await InitializeSessionAsync(httpClient, AddApiKey);
var listRequest = CreateListToolsRequest(sessionId);
- listRequest.Headers.TryAddWithoutValidation(McpApiKeyAuthenticationHandler.McpApiKeyHeaderName, apiKey);
+ AddApiKey(listRequest);
var responseMessage = await httpClient.SendAsync(listRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
responseMessage.EnsureSuccessStatusCode();
@@ -366,9 +338,10 @@ internal static HttpRequestMessage CreateInitializeRequest(string? sessionId = n
return request;
}
- internal static async Task InitializeSessionAsync(HttpClient httpClient)
+ internal static async Task InitializeSessionAsync(HttpClient httpClient, Action? configureRequest = null)
{
var initRequest = CreateInitializeRequest();
+ configureRequest?.Invoke(initRequest);
var initResponse = await httpClient.SendAsync(initRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
initResponse.EnsureSuccessStatusCode();
var sessionId = initResponse.Headers.GetValues("Mcp-Session-Id").First();
@@ -393,6 +366,7 @@ internal static async Task InitializeSessionAsync(HttpClient httpClient)
notificationRequest.Headers.TryAddWithoutValidation("accept", "application/json");
notificationRequest.Headers.TryAddWithoutValidation("accept", "text/event-stream");
notificationRequest.Headers.TryAddWithoutValidation("Mcp-Session-Id", sessionId);
+ configureRequest?.Invoke(notificationRequest);
var notificationResponse = await httpClient.SendAsync(notificationRequest).DefaultTimeout(TestConstants.LongTimeoutDuration);
notificationResponse.EnsureSuccessStatusCode();