diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index fa54c02e52..ce84976ca5 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -83,6 +83,7 @@
+
diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
index 657a84244d..b6b3b8048a 100644
--- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
+++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs
@@ -125,7 +125,7 @@ async Task InitializeServiceControl(ScenarioContext context)
hostBuilder.AddServiceControlAuthentication(settings.OpenIdConnectSettings);
hostBuilder.AddServiceControl(settings, configuration);
hostBuilder.AddServiceControlHttps(settings.HttpsSettings);
- hostBuilder.AddServiceControlApi(settings.CorsSettings);
+ hostBuilder.AddServiceControlApi(settings);
hostBuilder.AddServiceControlTesting(settings);
@@ -135,7 +135,7 @@ async Task InitializeServiceControl(ScenarioContext context)
host.UseTestRemoteIp();
host.UseServiceControlAuthentication(settings.OpenIdConnectSettings.Enabled);
- host.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings);
+ host.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings, settings.EnableMcpServer);
await host.StartAsync();
DomainEvents = host.Services.GetRequiredService();
// Bring this back and look into the base address of the client
diff --git a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
index 6873e229b3..5de2540e03 100644
--- a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
+++ b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt
@@ -37,6 +37,7 @@
},
"NotificationsFilter": null,
"AllowMessageEditing": false,
+ "EnableMcpServer": false,
"EnableIntegratedServicePulse": false,
"ServicePulseSettings": null,
"MessageFilter": null,
diff --git a/src/ServiceControl/App.config b/src/ServiceControl/App.config
index d6271805e5..698755c9a7 100644
--- a/src/ServiceControl/App.config
+++ b/src/ServiceControl/App.config
@@ -5,6 +5,8 @@ These settings are only here so that we can debug ServiceControl while developin
-->
+
+
diff --git a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs
index 105f756daf..932e301047 100644
--- a/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs
+++ b/src/ServiceControl/Hosting/Commands/ImportFailedErrorsCommand.cs
@@ -26,7 +26,7 @@ public override async Task Execute(HostArguments args, Settings settings)
var hostBuilder = Host.CreateApplicationBuilder();
hostBuilder.AddServiceControl(settings, endpointConfiguration);
- hostBuilder.AddServiceControlApi(settings.CorsSettings);
+ hostBuilder.AddServiceControlApi(settings);
using var app = hostBuilder.Build();
await app.StartAsync();
diff --git a/src/ServiceControl/Hosting/Commands/RunCommand.cs b/src/ServiceControl/Hosting/Commands/RunCommand.cs
index ebc08958cf..9778db2cc0 100644
--- a/src/ServiceControl/Hosting/Commands/RunCommand.cs
+++ b/src/ServiceControl/Hosting/Commands/RunCommand.cs
@@ -27,10 +27,10 @@ public override async Task Execute(HostArguments args, Settings settings)
hostBuilder.AddServiceControlAuthentication(settings.OpenIdConnectSettings);
hostBuilder.AddServiceControlHttps(settings.HttpsSettings);
hostBuilder.AddServiceControl(settings, endpointConfiguration);
- hostBuilder.AddServiceControlApi(settings.CorsSettings);
+ hostBuilder.AddServiceControlApi(settings);
var app = hostBuilder.Build();
- app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings);
+ app.UseServiceControl(settings.ForwardedHeadersSettings, settings.HttpsSettings, settings.EnableMcpServer);
if (settings.EnableIntegratedServicePulse)
{
app.UseServicePulse(settings.ServicePulseSettings);
diff --git a/src/ServiceControl/Infrastructure/Settings/Settings.cs b/src/ServiceControl/Infrastructure/Settings/Settings.cs
index d71b9dca66..24e7082863 100644
--- a/src/ServiceControl/Infrastructure/Settings/Settings.cs
+++ b/src/ServiceControl/Infrastructure/Settings/Settings.cs
@@ -81,6 +81,7 @@ public Settings(
DisableExternalIntegrationsPublishing = SettingsReader.Read(SettingsRootNamespace, "DisableExternalIntegrationsPublishing", false);
TrackInstancesInitialValue = SettingsReader.Read(SettingsRootNamespace, "TrackInstancesInitialValue", true);
ShutdownTimeout = SettingsReader.Read(SettingsRootNamespace, "ShutdownTimeout", ShutdownTimeout);
+ EnableMcpServer = SettingsReader.Read(SettingsRootNamespace, "EnableMcpServer", false);
AssemblyLoadContextResolver = static assemblyPath => new PluginAssemblyLoadContext(assemblyPath);
}
@@ -113,6 +114,8 @@ public Settings(
public bool AllowMessageEditing { get; set; }
+ public bool EnableMcpServer { get; set; }
+
public bool EnableIntegratedServicePulse { get; set; }
public ServicePulseSettings ServicePulseSettings { get; set; }
diff --git a/src/ServiceControl/Infrastructure/WebApi/HostApplicationBuilderExtensions.cs b/src/ServiceControl/Infrastructure/WebApi/HostApplicationBuilderExtensions.cs
index 298885ae0f..17dc44d5d3 100644
--- a/src/ServiceControl/Infrastructure/WebApi/HostApplicationBuilderExtensions.cs
+++ b/src/ServiceControl/Infrastructure/WebApi/HostApplicationBuilderExtensions.cs
@@ -9,10 +9,11 @@
using Microsoft.Extensions.Hosting;
using Particular.LicensingComponent.WebApi;
using Particular.ServiceControl;
+ using ServiceBus.Management.Infrastructure.Settings;
static class HostApplicationBuilderExtensions
{
- public static void AddServiceControlApi(this IHostApplicationBuilder builder, CorsSettings corsSettings)
+ public static void AddServiceControlApi(this IHostApplicationBuilder builder, Settings settings)
{
// This registers concrete classes that implement IApi. Currently it is hard to find out to what
// component those APIs should belong to so we leave it here for now.
@@ -20,7 +21,15 @@ public static void AddServiceControlApi(this IHostApplicationBuilder builder, Co
builder.AddServiceControlApis();
- builder.Services.AddCors(options => options.AddDefaultPolicy(Cors.GetDefaultPolicy(corsSettings)));
+ if (settings.EnableMcpServer)
+ {
+ builder.Services
+ .AddMcpServer()
+ .WithHttpTransport()
+ .WithToolsFromAssembly();
+ }
+
+ builder.Services.AddCors(options => options.AddDefaultPolicy(Cors.GetDefaultPolicy(settings.CorsSettings)));
// We're not explicitly adding Gzip here because it's already in the default list of supported compressors
builder.Services.AddResponseCompression();
diff --git a/src/ServiceControl/Mcp/ArchiveTools.cs b/src/ServiceControl/Mcp/ArchiveTools.cs
new file mode 100644
index 0000000000..86abe21de0
--- /dev/null
+++ b/src/ServiceControl/Mcp/ArchiveTools.cs
@@ -0,0 +1,90 @@
+namespace ServiceControl.Mcp;
+
+using System.ComponentModel;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MessageFailures.InternalMessages;
+using ModelContextProtocol.Server;
+using NServiceBus;
+using Persistence.Recoverability;
+using ServiceControl.Recoverability;
+
+[McpServerToolType]
+public class ArchiveTools(IMessageSession messageSession, IArchiveMessages archiver)
+{
+ [McpServerTool, Description("Archive a single failed message by its unique ID. The message will be moved to the archived status.")]
+ public async Task ArchiveFailedMessage(
+ [Description("The unique ID of the failed message to archive")] string failedMessageId)
+ {
+ await messageSession.SendLocal(m => m.FailedMessageId = failedMessageId);
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Archive requested for message '{failedMessageId}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Archive multiple failed messages by their unique IDs. All specified messages will be moved to the archived status.")]
+ public async Task ArchiveFailedMessages(
+ [Description("Array of unique message IDs to archive")] string[] messageIds)
+ {
+ if (messageIds.Any(string.IsNullOrEmpty))
+ {
+ return JsonSerializer.Serialize(new { Error = "All message IDs must be non-empty strings." }, McpJsonOptions.Default);
+ }
+
+ foreach (var id in messageIds)
+ {
+ await messageSession.SendLocal(m => m.FailedMessageId = id);
+ }
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Archive requested for {messageIds.Length} messages." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Archive all failed messages in a specific failure group. Failure groups are collections of messages grouped by exception type and stack trace.")]
+ public async Task ArchiveFailureGroup(
+ [Description("The ID of the failure group to archive")] string groupId)
+ {
+ if (archiver.IsOperationInProgressFor(groupId, ArchiveType.FailureGroup))
+ {
+ return JsonSerializer.Serialize(new { Status = "InProgress", Message = $"An archive operation is already in progress for group '{groupId}'." }, McpJsonOptions.Default);
+ }
+
+ await archiver.StartArchiving(groupId, ArchiveType.FailureGroup);
+ await messageSession.SendLocal(m => m.GroupId = groupId);
+
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Archive requested for all messages in failure group '{groupId}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Unarchive a single failed message by its unique ID. The message will be moved back to the unresolved status.")]
+ public async Task UnarchiveFailedMessage(
+ [Description("The unique ID of the failed message to unarchive")] string failedMessageId)
+ {
+ await messageSession.SendLocal(m => m.FailedMessageIds = [failedMessageId]);
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Unarchive requested for message '{failedMessageId}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Unarchive multiple failed messages by their unique IDs. All specified messages will be moved back to the unresolved status.")]
+ public async Task UnarchiveFailedMessages(
+ [Description("Array of unique message IDs to unarchive")] string[] messageIds)
+ {
+ if (messageIds.Any(string.IsNullOrEmpty))
+ {
+ return JsonSerializer.Serialize(new { Error = "All message IDs must be non-empty strings." }, McpJsonOptions.Default);
+ }
+
+ await messageSession.SendLocal(m => m.FailedMessageIds = messageIds);
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Unarchive requested for {messageIds.Length} messages." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Unarchive all failed messages in a specific failure group. Failure groups are collections of messages grouped by exception type and stack trace.")]
+ public async Task UnarchiveFailureGroup(
+ [Description("The ID of the failure group to unarchive")] string groupId)
+ {
+ if (archiver.IsOperationInProgressFor(groupId, ArchiveType.FailureGroup))
+ {
+ return JsonSerializer.Serialize(new { Status = "InProgress", Message = $"An archive operation is already in progress for group '{groupId}'." }, McpJsonOptions.Default);
+ }
+
+ await archiver.StartUnarchiving(groupId, ArchiveType.FailureGroup);
+ await messageSession.SendLocal(m => m.GroupId = groupId);
+
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Unarchive requested for all messages in failure group '{groupId}'." }, McpJsonOptions.Default);
+ }
+}
diff --git a/src/ServiceControl/Mcp/FailedMessageTools.cs b/src/ServiceControl/Mcp/FailedMessageTools.cs
new file mode 100644
index 0000000000..79c6d47e96
--- /dev/null
+++ b/src/ServiceControl/Mcp/FailedMessageTools.cs
@@ -0,0 +1,94 @@
+#nullable enable
+
+namespace ServiceControl.Mcp;
+
+using System.ComponentModel;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MessageFailures.Api;
+using ModelContextProtocol.Server;
+using Persistence;
+using Persistence.Infrastructure;
+
+[McpServerToolType]
+public class FailedMessageTools(IErrorMessageDataStore store)
+{
+ [McpServerTool, Description("Get a list of failed messages. Supports filtering by status (unresolved, resolved, archived, retryissued), modified date, and queue address. Returns paged results.")]
+ public async Task GetFailedMessages(
+ [Description("Filter by status: unresolved, resolved, archived, retryissued")] string? status = null,
+ [Description("Filter by modified date (ISO 8601 format)")] string? modified = null,
+ [Description("Filter by queue address")] string? queueAddress = null,
+ [Description("Page number (1-based). Default is 1")] int page = 1,
+ [Description("Number of results per page. Default is 50")] int perPage = 50,
+ [Description("Sort field: time_sent, message_type, time_of_failure. Default is time_of_failure")] string sort = "time_of_failure",
+ [Description("Sort direction: asc or desc. Default is desc")] string direction = "desc")
+ {
+ var pagingInfo = new PagingInfo(page, perPage);
+ var sortInfo = new SortInfo(sort, direction);
+
+ var results = await store.ErrorGet(status, modified, queueAddress, pagingInfo, sortInfo);
+
+ return JsonSerializer.Serialize(new
+ {
+ results.QueryStats.TotalCount,
+ results.Results
+ }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Get details of a specific failed message by its unique ID.")]
+ public async Task GetFailedMessageById(
+ [Description("The unique ID of the failed message")] string failedMessageId)
+ {
+ var result = await store.ErrorBy(failedMessageId);
+
+ if (result == null)
+ {
+ return JsonSerializer.Serialize(new { Error = $"Failed message '{failedMessageId}' not found." }, McpJsonOptions.Default);
+ }
+
+ return JsonSerializer.Serialize(result, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Get the last processing attempt for a specific failed message.")]
+ public async Task GetFailedMessageLastAttempt(
+ [Description("The unique ID of the failed message")] string failedMessageId)
+ {
+ var result = await store.ErrorLastBy(failedMessageId);
+
+ if (result == null)
+ {
+ return JsonSerializer.Serialize(new { Error = $"Failed message '{failedMessageId}' not found." }, McpJsonOptions.Default);
+ }
+
+ return JsonSerializer.Serialize(result, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Get a summary of error counts grouped by status (unresolved, archived, resolved, retryissued).")]
+ public async Task GetErrorsSummary()
+ {
+ var result = await store.ErrorsSummary();
+ return JsonSerializer.Serialize(result, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Get failed messages for a specific endpoint.")]
+ public async Task GetFailedMessagesByEndpoint(
+ [Description("The name of the endpoint")] string endpointName,
+ [Description("Filter by status: unresolved, resolved, archived, retryissued")] string? status = null,
+ [Description("Filter by modified date (ISO 8601 format)")] string? modified = null,
+ [Description("Page number (1-based). Default is 1")] int page = 1,
+ [Description("Number of results per page. Default is 50")] int perPage = 50,
+ [Description("Sort field: time_sent, message_type, time_of_failure. Default is time_of_failure")] string sort = "time_of_failure",
+ [Description("Sort direction: asc or desc. Default is desc")] string direction = "desc")
+ {
+ var pagingInfo = new PagingInfo(page, perPage);
+ var sortInfo = new SortInfo(sort, direction);
+
+ var results = await store.ErrorsByEndpointName(status, endpointName, modified, pagingInfo, sortInfo);
+
+ return JsonSerializer.Serialize(new
+ {
+ results.QueryStats.TotalCount,
+ results.Results
+ }, McpJsonOptions.Default);
+ }
+}
diff --git a/src/ServiceControl/Mcp/FailureGroupTools.cs b/src/ServiceControl/Mcp/FailureGroupTools.cs
new file mode 100644
index 0000000000..ec311f4ff8
--- /dev/null
+++ b/src/ServiceControl/Mcp/FailureGroupTools.cs
@@ -0,0 +1,30 @@
+#nullable enable
+
+namespace ServiceControl.Mcp;
+
+using System.ComponentModel;
+using System.Text.Json;
+using System.Threading.Tasks;
+using ModelContextProtocol.Server;
+using Persistence;
+using Recoverability;
+
+[McpServerToolType]
+public class FailureGroupTools(GroupFetcher fetcher, IRetryHistoryDataStore retryStore)
+{
+ [McpServerTool, Description("Get failure groups, which are collections of failed messages grouped by a classifier (default: exception type and stack trace). Each group shows the count of failures, the first and last occurrence, and any retry operation status.")]
+ public async Task GetFailureGroups(
+ [Description("The classifier to group by. Default is 'Exception Type and Stack Trace'")] string classifier = "Exception Type and Stack Trace",
+ [Description("Optional filter for the classifier")] string? classifierFilter = null)
+ {
+ var results = await fetcher.GetGroups(classifier, classifierFilter);
+ return JsonSerializer.Serialize(results, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Get the retry history showing past retry operations and their outcomes.")]
+ public async Task GetRetryHistory()
+ {
+ var retryHistory = await retryStore.GetRetryHistory();
+ return JsonSerializer.Serialize(retryHistory, McpJsonOptions.Default);
+ }
+}
diff --git a/src/ServiceControl/Mcp/McpJsonOptions.cs b/src/ServiceControl/Mcp/McpJsonOptions.cs
new file mode 100644
index 0000000000..1e37e52d37
--- /dev/null
+++ b/src/ServiceControl/Mcp/McpJsonOptions.cs
@@ -0,0 +1,14 @@
+namespace ServiceControl.Mcp;
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+static class McpJsonOptions
+{
+ public static JsonSerializerOptions Default { get; } = new()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ WriteIndented = false
+ };
+}
diff --git a/src/ServiceControl/Mcp/RetryTools.cs b/src/ServiceControl/Mcp/RetryTools.cs
new file mode 100644
index 0000000000..7d41f9d2f2
--- /dev/null
+++ b/src/ServiceControl/Mcp/RetryTools.cs
@@ -0,0 +1,84 @@
+namespace ServiceControl.Mcp;
+
+using System.ComponentModel;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using MessageFailures;
+using MessageFailures.InternalMessages;
+using ModelContextProtocol.Server;
+using NServiceBus;
+using Recoverability;
+using Persistence;
+
+[McpServerToolType]
+public class RetryTools(IMessageSession messageSession, RetryingManager retryingManager)
+{
+ [McpServerTool, Description("Retry a single failed message by its unique ID. The message will be sent back to its original queue for reprocessing.")]
+ public async Task RetryFailedMessage(
+ [Description("The unique ID of the failed message to retry")] string failedMessageId)
+ {
+ await messageSession.SendLocal(m => m.FailedMessageId = failedMessageId);
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Retry requested for message '{failedMessageId}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Retry multiple failed messages by their unique IDs. All specified messages will be sent back to their original queues for reprocessing.")]
+ public async Task RetryFailedMessages(
+ [Description("Array of unique message IDs to retry")] string[] messageIds)
+ {
+ if (messageIds.Any(string.IsNullOrEmpty))
+ {
+ return JsonSerializer.Serialize(new { Error = "All message IDs must be non-empty strings." }, McpJsonOptions.Default);
+ }
+
+ await messageSession.SendLocal(m => m.MessageUniqueIds = messageIds);
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Retry requested for {messageIds.Length} messages." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Retry all failed messages from a specific queue address.")]
+ public async Task RetryFailedMessagesByQueue(
+ [Description("The queue address to retry all failed messages from")] string queueAddress)
+ {
+ await messageSession.SendLocal(m =>
+ {
+ m.QueueAddress = queueAddress;
+ m.Status = FailedMessageStatus.Unresolved;
+ });
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Retry requested for all failed messages in queue '{queueAddress}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Retry all failed messages across all queues. Use with caution as this affects all unresolved failed messages.")]
+ public async Task RetryAllFailedMessages()
+ {
+ await messageSession.SendLocal(new RequestRetryAll());
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = "Retry requested for all failed messages." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Retry all failed messages for a specific endpoint.")]
+ public async Task RetryAllFailedMessagesByEndpoint(
+ [Description("The name of the endpoint to retry all failed messages for")] string endpointName)
+ {
+ await messageSession.SendLocal(new RequestRetryAll { Endpoint = endpointName });
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Retry requested for all failed messages in endpoint '{endpointName}'." }, McpJsonOptions.Default);
+ }
+
+ [McpServerTool, Description("Retry all failed messages in a specific failure group. Failure groups are collections of messages grouped by exception type and stack trace.")]
+ public async Task RetryFailureGroup(
+ [Description("The ID of the failure group to retry")] string groupId)
+ {
+ if (retryingManager.IsOperationInProgressFor(groupId, RetryType.FailureGroup))
+ {
+ return JsonSerializer.Serialize(new { Status = "InProgress", Message = $"A retry operation is already in progress for group '{groupId}'." }, McpJsonOptions.Default);
+ }
+
+ var started = System.DateTime.UtcNow;
+ await retryingManager.Wait(groupId, RetryType.FailureGroup, started);
+ await messageSession.SendLocal(new RetryAllInGroup
+ {
+ GroupId = groupId,
+ Started = started
+ });
+
+ return JsonSerializer.Serialize(new { Status = "Accepted", Message = $"Retry requested for all messages in failure group '{groupId}'." }, McpJsonOptions.Default);
+ }
+}
diff --git a/src/ServiceControl/MessageFailures/Handlers/ArchiveMessageHandler.cs b/src/ServiceControl/MessageFailures/Handlers/ArchiveMessageHandler.cs
index 2e317cba54..ae852de26e 100644
--- a/src/ServiceControl/MessageFailures/Handlers/ArchiveMessageHandler.cs
+++ b/src/ServiceControl/MessageFailures/Handlers/ArchiveMessageHandler.cs
@@ -21,7 +21,7 @@ public async Task Handle(ArchiveMessage message, IMessageHandlerContext context)
var failedMessage = await dataStore.ErrorBy(failedMessageId);
- if (failedMessage.Status != FailedMessageStatus.Archived)
+ if (failedMessage is not null && failedMessage.Status != FailedMessageStatus.Archived)
{
await domainEvents.Raise(new FailedMessageArchived
{
diff --git a/src/ServiceControl/ServiceControl.csproj b/src/ServiceControl/ServiceControl.csproj
index d931751d34..2475998650 100644
--- a/src/ServiceControl/ServiceControl.csproj
+++ b/src/ServiceControl/ServiceControl.csproj
@@ -33,6 +33,7 @@
+
diff --git a/src/ServiceControl/WebApplicationExtensions.cs b/src/ServiceControl/WebApplicationExtensions.cs
index 685bc7dc16..ac015a5c5b 100644
--- a/src/ServiceControl/WebApplicationExtensions.cs
+++ b/src/ServiceControl/WebApplicationExtensions.cs
@@ -9,7 +9,7 @@ namespace ServiceControl;
public static class WebApplicationExtensions
{
- public static void UseServiceControl(this WebApplication app, ForwardedHeadersSettings forwardedHeadersSettings, HttpsSettings httpsSettings)
+ public static void UseServiceControl(this WebApplication app, ForwardedHeadersSettings forwardedHeadersSettings, HttpsSettings httpsSettings, bool enableMcpServer)
{
app.UseServiceControlForwardedHeaders(forwardedHeadersSettings);
app.UseServiceControlHttps(httpsSettings);
@@ -19,5 +19,10 @@ public static void UseServiceControl(this WebApplication app, ForwardedHeadersSe
app.MapHub("/api/messagestream");
app.UseCors();
app.MapControllers();
+
+ if (enableMcpServer)
+ {
+ app.MapMcp();
+ }
}
}
\ No newline at end of file