123 lines
5.1 KiB
C#
123 lines
5.1 KiB
C#
using System.ComponentModel;
|
|
using ColaFlow.Modules.Mcp.Application.DTOs;
|
|
using ColaFlow.Modules.Mcp.Application.Services;
|
|
using ColaFlow.Modules.Mcp.Domain.Exceptions;
|
|
using ColaFlow.Modules.Mcp.Domain.Services;
|
|
using ColaFlow.Modules.IssueManagement.Domain.Enums;
|
|
using ColaFlow.Modules.IssueManagement.Domain.Repositories;
|
|
using Microsoft.Extensions.Logging;
|
|
using ModelContextProtocol.Server;
|
|
|
|
namespace ColaFlow.Modules.Mcp.Application.SdkTools;
|
|
|
|
/// <summary>
|
|
/// MCP Tool: update_status (SDK-based implementation)
|
|
/// Updates the status of an existing Issue
|
|
/// Generates a Diff Preview and creates a PendingChange for approval
|
|
/// </summary>
|
|
[McpServerToolType]
|
|
public class UpdateStatusSdkTool
|
|
{
|
|
private readonly IPendingChangeService _pendingChangeService;
|
|
private readonly IIssueRepository _issueRepository;
|
|
private readonly DiffPreviewService _diffPreviewService;
|
|
private readonly ILogger<UpdateStatusSdkTool> _logger;
|
|
|
|
public UpdateStatusSdkTool(
|
|
IPendingChangeService pendingChangeService,
|
|
IIssueRepository issueRepository,
|
|
DiffPreviewService diffPreviewService,
|
|
ILogger<UpdateStatusSdkTool> logger)
|
|
{
|
|
_pendingChangeService = pendingChangeService ?? throw new ArgumentNullException(nameof(pendingChangeService));
|
|
_issueRepository = issueRepository ?? throw new ArgumentNullException(nameof(issueRepository));
|
|
_diffPreviewService = diffPreviewService ?? throw new ArgumentNullException(nameof(diffPreviewService));
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
}
|
|
|
|
[McpServerTool]
|
|
[Description("Update the status of an existing issue. Supports workflow transitions (Backlog → Todo → InProgress → Done). Requires human approval before being applied.")]
|
|
public async Task<string> UpdateStatusAsync(
|
|
[Description("The ID of the issue to update")] Guid issueId,
|
|
[Description("The new status: Backlog, Todo, InProgress, or Done")] string newStatus,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Executing update_status tool (SDK)");
|
|
|
|
// 1. Validate and parse status
|
|
if (!Enum.TryParse<IssueStatus>(newStatus, ignoreCase: true, out var statusEnum))
|
|
throw new McpInvalidParamsException($"Invalid status: {newStatus}. Must be Backlog, Todo, InProgress, or Done");
|
|
|
|
// 2. Fetch current issue
|
|
var issue = await _issueRepository.GetByIdAsync(issueId, cancellationToken);
|
|
if (issue == null)
|
|
throw new McpNotFoundException("Issue", issueId.ToString());
|
|
|
|
var oldStatus = issue.Status;
|
|
|
|
// 3. Build before and after data for diff preview
|
|
var beforeData = new
|
|
{
|
|
id = issue.Id,
|
|
title = issue.Title,
|
|
type = issue.Type.ToString(),
|
|
status = oldStatus.ToString(),
|
|
priority = issue.Priority.ToString()
|
|
};
|
|
|
|
var afterData = new
|
|
{
|
|
id = issue.Id,
|
|
title = issue.Title,
|
|
type = issue.Type.ToString(),
|
|
status = statusEnum.ToString(), // Only status changed
|
|
priority = issue.Priority.ToString()
|
|
};
|
|
|
|
// 4. Generate Diff Preview (UPDATE operation)
|
|
var diff = _diffPreviewService.GenerateUpdateDiff(
|
|
entityType: "Issue",
|
|
entityId: issueId,
|
|
beforeEntity: beforeData,
|
|
afterEntity: afterData,
|
|
entityKey: $"{issue.Type}-{issue.Id.ToString().Substring(0, 8)}"
|
|
);
|
|
|
|
// 5. Create PendingChange
|
|
var pendingChange = await _pendingChangeService.CreateAsync(
|
|
new CreatePendingChangeRequest
|
|
{
|
|
ToolName = "update_status",
|
|
Diff = diff,
|
|
ExpirationHours = 24
|
|
},
|
|
cancellationToken);
|
|
|
|
_logger.LogInformation(
|
|
"PendingChange created: {PendingChangeId} - UPDATE Issue {IssueId} status: {OldStatus} → {NewStatus}",
|
|
pendingChange.Id, issueId, oldStatus, statusEnum);
|
|
|
|
// 6. Return pendingChangeId to AI
|
|
return $"Issue status update request submitted for approval.\n\n" +
|
|
$"**Pending Change ID**: {pendingChange.Id}\n" +
|
|
$"**Status**: Pending Approval\n" +
|
|
$"**Issue**: {issue.Title}\n" +
|
|
$"**Old Status**: {oldStatus}\n" +
|
|
$"**New Status**: {statusEnum}\n\n" +
|
|
$"A human user must approve this change before the issue status is updated. " +
|
|
$"The change will expire at {pendingChange.ExpiresAt:yyyy-MM-dd HH:mm} UTC if not approved.";
|
|
}
|
|
catch (McpException)
|
|
{
|
|
throw; // Re-throw MCP exceptions as-is
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error executing update_status tool (SDK)");
|
|
throw new McpInvalidParamsException($"Error updating issue status: {ex.Message}");
|
|
}
|
|
}
|
|
}
|