feat(backend): Implement SignalR Real-Time Notifications for MCP - Story 5.12
Implemented comprehensive real-time notification system using SignalR to notify AI agents and users about PendingChange status updates. Key Features Implemented: - McpNotificationHub with Subscribe/Unsubscribe methods - Real-time notifications for all PendingChange lifecycle events - Tenant-based isolation for multi-tenancy security - Notification DTOs for structured message formats - Domain event handlers for automatic notification sending - Comprehensive unit tests for notification service and handlers - Client integration guide with examples for TypeScript, React, and Python Components Created: 1. SignalR Hub: - McpNotificationHub.cs - Central hub for MCP notifications 2. Notification DTOs: - PendingChangeNotification.cs (base class) - PendingChangeCreatedNotification.cs - PendingChangeApprovedNotification.cs - PendingChangeRejectedNotification.cs - PendingChangeAppliedNotification.cs - PendingChangeExpiredNotification.cs 3. Notification Service: - IMcpNotificationService.cs (interface) - McpNotificationService.cs (implementation using SignalR) 4. Event Handlers (send notifications): - PendingChangeCreatedNotificationHandler.cs - PendingChangeApprovedNotificationHandler.cs - PendingChangeRejectedNotificationHandler.cs - PendingChangeAppliedNotificationHandler.cs - PendingChangeExpiredNotificationHandler.cs 5. Tests: - McpNotificationServiceTests.cs - Unit tests for notification service - PendingChangeCreatedNotificationHandlerTests.cs - PendingChangeApprovedNotificationHandlerTests.cs 6. Documentation: - signalr-mcp-client-guide.md - Comprehensive client integration guide Technical Details: - Hub endpoint: /hubs/mcp-notifications - Authentication: JWT token via query string (?access_token=xxx) - Tenant isolation: Automatic group joining based on tenant ID - Group subscriptions: Per-pending-change and per-tenant groups - Notification delivery: < 1 second (real-time) - Fallback strategy: Polling if WebSocket unavailable Architecture Benefits: - Decoupled design using domain events - Notification failures don't break main flow - Scalable (supports Redis backplane for multi-instance) - Type-safe notification payloads - Tenant isolation built-in Story: Phase 3 - Tools & Diff Preview Priority: P0 CRITICAL Story Points: 3 Completion: 100% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent when a PendingChange has been successfully applied
|
||||
/// (after approval and execution)
|
||||
/// </summary>
|
||||
public sealed record PendingChangeAppliedNotification : PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of applying the change
|
||||
/// </summary>
|
||||
public required string Result { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the change was applied (UTC)
|
||||
/// </summary>
|
||||
public required DateTime AppliedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent when a PendingChange is approved and executed
|
||||
/// </summary>
|
||||
public sealed record PendingChangeApprovedNotification : PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of entity that was changed
|
||||
/// </summary>
|
||||
public required string EntityType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation that was performed
|
||||
/// </summary>
|
||||
public required string Operation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// ID of the entity that was created/updated (if applicable)
|
||||
/// </summary>
|
||||
public Guid? EntityId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// ID of the user who approved the change
|
||||
/// </summary>
|
||||
public required Guid ApprovedBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Result of executing the change (e.g., "Epic created: {id} - {name}")
|
||||
/// </summary>
|
||||
public string? ExecutionResult { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent when a new PendingChange is created
|
||||
/// </summary>
|
||||
public sealed record PendingChangeCreatedNotification : PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of entity being changed (Epic, Story, Task, etc.)
|
||||
/// </summary>
|
||||
public required string EntityType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation being performed (CREATE, UPDATE, DELETE)
|
||||
/// </summary>
|
||||
public required string Operation { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Summary of what will be changed
|
||||
/// </summary>
|
||||
public required string Summary { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent when a PendingChange expires (timeout)
|
||||
/// </summary>
|
||||
public sealed record PendingChangeExpiredNotification : PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// When the pending change expired (UTC)
|
||||
/// </summary>
|
||||
public required DateTime ExpiredAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all PendingChange notifications
|
||||
/// </summary>
|
||||
public abstract record PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of notification (PendingChangeCreated, PendingChangeApproved, etc.)
|
||||
/// </summary>
|
||||
public required string NotificationType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the pending change
|
||||
/// </summary>
|
||||
public required Guid PendingChangeId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The tool that created the pending change
|
||||
/// </summary>
|
||||
public required string ToolName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When this notification was generated (UTC)
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; init; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Tenant ID for multi-tenancy support
|
||||
/// </summary>
|
||||
public required Guid TenantId { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace ColaFlow.Modules.Mcp.Application.DTOs.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent when a PendingChange is rejected
|
||||
/// </summary>
|
||||
public sealed record PendingChangeRejectedNotification : PendingChangeNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Reason for rejection
|
||||
/// </summary>
|
||||
public required string Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// ID of the user who rejected the change
|
||||
/// </summary>
|
||||
public required Guid RejectedBy { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user