Skip to main content
Glama

COA Goldfish MCP

by anortham
PlanTool.csโ€ข12.9 kB
using COA.Goldfish.McpServer.Services.Storage; using COA.Goldfish.McpServer.Models; using COA.Mcp.Framework.Models; using Microsoft.Extensions.Logging; namespace COA.Goldfish.McpServer.Tools; /// <summary> /// Tool for plan operations - strategic planning /// </summary> public class PlanTool : GoldfishToolBase<PlanParameters, PlanResult> { private readonly IStorageService _storage; private readonly ILogger<PlanTool> _logger; public PlanTool(IServiceProvider serviceProvider, ILogger<PlanTool> logger, IStorageService storage) : base(serviceProvider, logger) { _storage = storage; _logger = logger; } public override string Name => "plan"; public override string Description => "PROACTIVE planning tool for complex features and architecture. Use BEFORE coding to design implementation approach, break down requirements, identify risks, and generate TODO lists. Different from checkpoint: plan = future work design, checkpoint = current progress save. Essential for features requiring multiple files/steps."; protected override async Task<PlanResult> ExecuteInternalAsync( PlanParameters parameters, CancellationToken cancellationToken) { try { var workspaceId = parameters.Workspace ?? "default"; var result = new PlanResult(); switch (parameters.Action.ToLowerInvariant()) { case "save": case "create": if (string.IsNullOrEmpty(parameters.Title)) { result.Success = false; result.Error = new ErrorInfo { Code = "MISSING_TITLE", Message = "Title is required for save action" }; return result; } if (string.IsNullOrEmpty(parameters.Description)) { result.Success = false; result.Error = new ErrorInfo { Code = "MISSING_DESCRIPTION", Message = "Description is required for save action" }; return result; } // Deactivate current active plan var currentActive = await _storage.GetActivePlanAsync(workspaceId); if (currentActive != null) { currentActive.Status = PlanStatus.Complete; await _storage.SavePlanAsync(currentActive); } var plan = new Plan { Id = Guid.NewGuid().ToString(), WorkspaceId = workspaceId, Title = parameters.Title, Description = parameters.Description, Category = parameters.Category ?? "general", Priority = parameters.Priority, Status = PlanStatus.Active, Items = parameters.Items ?? new List<string>() }; result.Plan = await _storage.SavePlanAsync(plan); result.Message = $"Created plan '{parameters.Title}' with {plan.Items.Count} items"; result.Success = true; break; case "list": result.Plans = await _storage.GetPlansAsync(workspaceId, includeCompleted: true); result.Message = $"Found {result.Plans.Count} plans"; result.Success = true; break; case "update": if (string.IsNullOrEmpty(parameters.PlanId)) { result.Success = false; result.Error = new ErrorInfo { Code = "MISSING_PLAN_ID", Message = "PlanId is required for update action" }; return result; } // Resolve smart keywords for plans Plan? targetPlan = null; if (parameters.PlanId.ToLowerInvariant() is "latest" or "recent" or "last") { var recentPlans = await _storage.GetPlansAsync(workspaceId, includeCompleted: true); targetPlan = recentPlans.OrderByDescending(p => p.UpdatedAt).FirstOrDefault(); } else if (parameters.PlanId.ToLowerInvariant() is "active" or "current") { targetPlan = await _storage.GetActivePlanAsync(workspaceId); } else { // Try direct lookup first, then keyword resolution targetPlan = await _storage.GetPlanAsync(parameters.PlanId); if (targetPlan == null) { targetPlan = await _storage.ResolvePlanKeywordAsync(workspaceId, parameters.PlanId); } } if (targetPlan == null) { result.Success = false; result.Error = new ErrorInfo { Code = "PLAN_NOT_FOUND", Message = $"Plan '{parameters.PlanId}' not found" }; return result; } // Update plan fields as provided if (!string.IsNullOrEmpty(parameters.Title)) targetPlan.Title = parameters.Title; if (!string.IsNullOrEmpty(parameters.Description)) targetPlan.Description = parameters.Description; if (!string.IsNullOrEmpty(parameters.Category)) targetPlan.Category = parameters.Category; if (!string.IsNullOrEmpty(parameters.Priority)) targetPlan.Priority = parameters.Priority; if (parameters.Items != null) targetPlan.Items = parameters.Items; if (parameters.Discoveries != null) targetPlan.Discoveries = parameters.Discoveries; targetPlan.UpdatedAt = DateTime.UtcNow; result.Plan = await _storage.SavePlanAsync(targetPlan); result.Message = $"Updated plan '{targetPlan.Title}'"; result.Success = true; break; case "complete": if (string.IsNullOrEmpty(parameters.PlanId)) { result.Success = false; result.Error = new ErrorInfo { Code = "MISSING_PLAN_ID", Message = "PlanId is required for complete action" }; return result; } var planToComplete = await ResolvePlanAsync(workspaceId, parameters.PlanId); if (planToComplete == null) { result.Success = false; result.Error = new ErrorInfo { Code = "PLAN_NOT_FOUND", Message = $"Plan '{parameters.PlanId}' not found" }; return result; } planToComplete.Status = PlanStatus.Complete; planToComplete.UpdatedAt = DateTime.UtcNow; result.Plan = await _storage.SavePlanAsync(planToComplete); result.Message = $"Completed plan '{planToComplete.Title}'"; result.Success = true; break; case "generate-todos": if (string.IsNullOrEmpty(parameters.PlanId)) { result.Success = false; result.Error = new ErrorInfo { Code = "MISSING_PLAN_ID", Message = "PlanId is required for generate-todos action" }; return result; } var sourcePlan = await ResolvePlanAsync(workspaceId, parameters.PlanId); if (sourcePlan == null) { result.Success = false; result.Error = new ErrorInfo { Code = "PLAN_NOT_FOUND", Message = $"Plan '{parameters.PlanId}' not found" }; return result; } if (sourcePlan.Items.Count == 0) { result.Success = false; result.Error = new ErrorInfo { Code = "NO_ITEMS", Message = "Plan has no items to generate TODOs from" }; return result; } // Deactivate current active TODO list var currentActiveTodos = await _storage.GetActiveTodoListAsync(workspaceId); if (currentActiveTodos != null) { currentActiveTodos.IsActive = false; await _storage.SaveTodoListAsync(currentActiveTodos); } // Create TODO list from plan items var todoList = new TodoList { Id = Guid.NewGuid().ToString(), WorkspaceId = workspaceId, Title = $"Tasks for: {sourcePlan.Title}", Description = $"Generated from plan '{sourcePlan.Title}'", IsActive = true, Items = sourcePlan.Items.Select(item => new TodoItem { Id = Guid.NewGuid().ToString(), Content = item, Status = TodoItemStatus.Pending, Priority = TodoItemPriority.Normal }).ToList() }; // Set navigation properties foreach (var item in todoList.Items) { item.TodoListId = todoList.Id; item.TodoList = todoList; } result.GeneratedTodos = await _storage.SaveTodoListAsync(todoList); result.Message = $"Generated TODO list with {sourcePlan.Items.Count} tasks from plan '{sourcePlan.Title}'"; result.Success = true; break; default: result.Success = false; result.Error = new ErrorInfo { Code = "INVALID_ACTION", Message = $"Unknown action: {parameters.Action}" }; break; } return result; } catch (Exception ex) { _logger.LogError(ex, "Plan operation failed"); return new PlanResult { Success = false, Error = new ErrorInfo { Code = "PLAN_ERROR", Message = ex.Message } }; } } private async Task<Plan?> ResolvePlanAsync(string workspaceId, string planId) { if (planId.ToLowerInvariant() is "latest" or "recent" or "last") { var recentPlans = await _storage.GetPlansAsync(workspaceId, includeCompleted: true); return recentPlans.OrderByDescending(p => p.UpdatedAt).FirstOrDefault(); } else if (planId.ToLowerInvariant() is "active" or "current") { return await _storage.GetActivePlanAsync(workspaceId); } else { var plan = await _storage.GetPlanAsync(planId); if (plan == null) { plan = await _storage.ResolvePlanKeywordAsync(workspaceId, planId); } return plan; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anortham/coa-goldfish-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server