using System.ComponentModel; using System.Text.Json; using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces; using ColaFlow.Modules.ProjectManagement.Domain.Repositories; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; namespace ColaFlow.Modules.Mcp.Application.SdkResources; /// /// MCP Resource: Projects (SDK-based implementation) /// Provides access to project data in the current tenant /// [McpServerResourceType] public class ProjectsSdkResource { private readonly IProjectRepository _projectRepository; private readonly ITenantContext _tenantContext; private readonly ILogger _logger; public ProjectsSdkResource( IProjectRepository projectRepository, ITenantContext tenantContext, ILogger logger) { _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository)); _tenantContext = tenantContext ?? throw new ArgumentNullException(nameof(tenantContext)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } [McpServerResource] [Description("List all projects in current tenant")] public async Task ListProjectsAsync(CancellationToken cancellationToken = default) { var tenantId = _tenantContext.GetCurrentTenantId(); _logger.LogDebug("Fetching projects list for tenant {TenantId} (SDK)", tenantId); // Get all projects (read-only) var projects = await _projectRepository.GetAllProjectsReadOnlyAsync(cancellationToken); // Map to DTOs var projectDtos = projects.Select(p => new { id = p.Id.Value, name = p.Name, key = p.Key.ToString(), description = p.Description, status = p.Status.ToString(), createdAt = p.CreatedAt, updatedAt = p.UpdatedAt, epicCount = p.Epics?.Count ?? 0 }).ToList(); var result = JsonSerializer.Serialize(new { projects = projectDtos, total = projectDtos.Count }, new JsonSerializerOptions { WriteIndented = true }); _logger.LogInformation("Retrieved {Count} projects for tenant {TenantId} (SDK)", projectDtos.Count, tenantId); return result; } [McpServerResource] [Description("Get detailed information about a specific project")] public async Task GetProjectAsync( [Description("The project ID")] Guid projectId, CancellationToken cancellationToken = default) { var tenantId = _tenantContext.GetCurrentTenantId(); _logger.LogDebug("Fetching project {ProjectId} for tenant {TenantId} (SDK)", projectId, tenantId); var project = await _projectRepository.GetByIdAsync( ProjectManagement.Domain.ValueObjects.ProjectId.From(projectId), cancellationToken); if (project == null) { throw new InvalidOperationException($"Project with ID {projectId} not found"); } var result = JsonSerializer.Serialize(new { id = project.Id.Value, name = project.Name, key = project.Key.ToString(), description = project.Description, status = project.Status.ToString(), createdAt = project.CreatedAt, updatedAt = project.UpdatedAt, epics = project.Epics?.Select(e => new { id = e.Id.Value, title = e.Name, // Epic uses Name instead of Title status = e.Status.ToString() }).ToList() ?? (object)new List() }, new JsonSerializerOptions { WriteIndented = true }); _logger.LogInformation("Retrieved project {ProjectId} for tenant {TenantId} (SDK)", projectId, tenantId); return result; } }