Skip to main content
Glama
smart-actions.ts8.86 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { SuperProductivityClient } from "../client/sp-client.js"; export function setupSmartActions( server: McpServer, client: SuperProductivityClient, ) { // Tool: Análise inteligente de produtividade server.tool( "analyze_productivity", { days: z.number().default(7).describe("Number of days to analyze"), }, async ({ days }) => { try { const tasks = await client.getTasks(); const now = Date.now(); const startDate = now - days * 24 * 60 * 60 * 1000; const recentTasks = tasks.filter( (t) => t.created >= startDate || (t.doneOn && t.doneOn >= startDate), ); const completedTasks = recentTasks.filter((t) => t.isDone); const totalEstimated = recentTasks.reduce( (sum, t) => sum + (t.timeEstimate || 0), 0, ); const totalSpent = recentTasks.reduce((sum, t) => sum + t.timeSpent, 0); const analysis = { period: `${days} days`, totalTasks: recentTasks.length, completedTasks: completedTasks.length, completionRate: recentTasks.length > 0 ? `${((completedTasks.length / recentTasks.length) * 100).toFixed(1)}%` : "0%", totalTimeEstimated: `${(totalEstimated / (1000 * 60 * 60)).toFixed(1)} hours`, totalTimeSpent: `${(totalSpent / (1000 * 60 * 60)).toFixed(1)} hours`, estimationAccuracy: totalEstimated > 0 ? `${((totalSpent / totalEstimated) * 100).toFixed(1)}%` : "N/A", insights: generateInsights( recentTasks, completedTasks, totalEstimated, totalSpent, ), }; return { content: [ { type: "text", text: JSON.stringify(analysis, null, 2), }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } }, ); // Tool: Sugerir priorização server.tool( "suggest_priorities", { projectId: z.string().optional(), maxTasks: z.number().default(5), }, async ({ projectId, maxTasks }) => { try { let tasks = await client.getCurrentContextTasks(); if (projectId) { tasks = tasks.filter((t) => t.projectId === projectId); } // Filtrar apenas tarefas não concluídas const pendingTasks = tasks.filter((t) => !t.isDone); // Algoritmo de priorização simples const scoredTasks = pendingTasks.map((task) => { let score = 0; // Tarefas com deadline próximo (se tiver campo de data) if (task.dueDate) { const daysUntilDue = (task.dueDate - Date.now()) / (1000 * 60 * 60 * 24); if (daysUntilDue < 1) score += 50; else if (daysUntilDue < 3) score += 30; else if (daysUntilDue < 7) score += 10; } // Tarefas sem estimativa de tempo (precisam ser avaliadas) if (!task.timeEstimate) score += 15; // Tarefas com subtarefas incompletas if (task.subTaskIds && task.subTaskIds.length > 0) score += 20; // Tarefas mais antigas const ageInDays = (Date.now() - task.created) / (1000 * 60 * 60 * 24); if (ageInDays > 7) score += 10; if (ageInDays > 14) score += 15; return { task, score }; }); // Ordenar por score e pegar top N const topTasks = scoredTasks .sort((a, b) => b.score - a.score) .slice(0, maxTasks) .map(({ task, score }) => ({ id: task.id, title: task.title, priorityScore: score, reasons: explainPriority(task, score), })); return { content: [ { type: "text", text: JSON.stringify( { suggestions: topTasks, message: `Top ${topTasks.length} priority tasks identified`, }, null, 2, ), }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } }, ); // Tool: Criar plano de trabalho diário server.tool( "create_daily_plan", { availableHours: z.number().default(8).describe("Hours available to work"), includeBreaks: z.boolean().default(true), }, async ({ availableHours, includeBreaks }) => { try { const tasks = await client.getCurrentContextTasks(); const pendingTasks = tasks.filter((t) => !t.isDone); const availableMinutes = availableHours * 60; const breakMinutes = includeBreaks ? Math.floor(availableMinutes * 0.15) : 0; const workMinutes = availableMinutes - breakMinutes; // Selecionar tarefas que cabem no tempo disponível const selectedTasks = []; let totalTime = 0; for (const task of pendingTasks) { const estimatedMinutes = (task.timeEstimate || 3600000) / (1000 * 60); if (totalTime + estimatedMinutes <= workMinutes) { selectedTasks.push({ id: task.id, title: task.title, estimatedMinutes: Math.round(estimatedMinutes), order: selectedTasks.length + 1, }); totalTime += estimatedMinutes; } } const plan = { date: new Date().toISOString().split("T")[0], totalAvailableTime: `${availableHours} hours`, workTime: `${Math.round(workMinutes / 60)} hours`, breakTime: includeBreaks ? `${Math.round(breakMinutes / 60)} hours` : "None", plannedTasks: selectedTasks, totalPlannedTime: `${Math.round(totalTime / 60)} hours ${Math.round(totalTime % 60)} minutes`, utilizationRate: `${((totalTime / workMinutes) * 100).toFixed(1)}%`, }; return { content: [ { type: "text", text: JSON.stringify(plan, null, 2), }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error: ${error.message}`, }, ], isError: true, }; } }, ); } function generateInsights( recentTasks: any[], completedTasks: any[], totalEstimated: number, totalSpent: number, ): string[] { const insights = []; const completionRate = recentTasks.length ? completedTasks.length / recentTasks.length : 0; if (completionRate > 0.8) { insights.push( "Excelente taxa de conclusão! Você está mantendo um ótimo ritmo.", ); } else if (completionRate < 0.4) { insights.push( "Taxa de conclusão baixa. Considere revisar suas estimativas ou reduzir o número de tarefas.", ); } if (totalEstimated > 0) { const accuracy = totalSpent / totalEstimated; if (accuracy > 1.2) { insights.push( "Você está subestimando o tempo necessário. Considere adicionar buffers às estimativas.", ); } else if (accuracy < 0.8) { insights.push( "Você está superestimando o tempo. Suas tarefas estão sendo concluídas mais rápido que o esperado.", ); } } const tasksWithoutEstimate = recentTasks.filter( (t) => !t.timeEstimate, ).length; if (tasksWithoutEstimate > recentTasks.length * 0.3) { insights.push( `${tasksWithoutEstimate} tarefas sem estimativa de tempo. Adicionar estimativas ajuda no planejamento.`, ); } return insights; } function explainPriority(task: any, score: number): string[] { const reasons = []; if (task.dueDate) { const daysUntilDue = (task.dueDate - Date.now()) / (1000 * 60 * 60 * 24); if (daysUntilDue < 1) reasons.push("Deadline urgente (< 24h)"); else if (daysUntilDue < 3) reasons.push("Deadline próximo (< 3 dias)"); } if (!task.timeEstimate) reasons.push("Sem estimativa de tempo"); if (task.subTaskIds && task.subTaskIds.length > 0) { reasons.push(`Possui ${task.subTaskIds.length} subtarefas`); } const ageInDays = (Date.now() - task.created) / (1000 * 60 * 60 * 24); if (ageInDays > 14) reasons.push("Tarefa antiga (> 2 semanas)"); return reasons; }

Latest Blog Posts

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/rochadelon/super-productivity-mcp'

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