Skip to main content
Glama

gitea_workflow_generate_report

Generate workflow reports for Gitea repositories with issue statistics, health scores, and actionable recommendations in JSON and Markdown formats.

Instructions

Generate a comprehensive workflow report including issue statistics, health score, and recommendations. Output in JSON and Markdown formats.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ownerNoRepository owner. Uses context if not provided
repoNoRepository name. Uses context if not provided
time_rangeNoTime range for statistics (default: all time)

Implementation Reference

  • The core handler function `workflowGenerateReport` that implements the tool logic. It loads config, fetches open/closed issues, computes statistics by status/priority/type, calculates average age, blocked count, health score, generates recommendations, and produces a Markdown report.
    export async function workflowGenerateReport( ctx: WorkflowToolsContext, args: { owner?: string; repo?: string; config?: WorkflowConfig; time_range?: 'day' | 'week' | 'month'; } ): Promise<{ success: boolean; report: { summary: { total_open: number; total_closed: number; by_status: Record<string, number>; by_priority: Record<string, number>; by_type: Record<string, number>; }; blocked_count: number; avg_age_days: number; health_score: number; recommendations: string[]; }; markdown_report: string; error?: string; }> { logger.debug({ args: { ...args, config: args.config ? '[provided]' : undefined } }, 'Generating report'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); // 获取配置 let config = args.config; if (!config) { const loadResult = await workflowLoadConfig(ctx, { owner, repo }); if (!loadResult.success || !loadResult.config) { return { success: false, report: { summary: { total_open: 0, total_closed: 0, by_status: {}, by_priority: {}, by_type: {} }, blocked_count: 0, avg_age_days: 0, health_score: 0, recommendations: [], }, markdown_report: '', error: loadResult.error || '无法加载配置', }; } config = loadResult.config; } try { // 获取开放和关闭的 Issue const openIssues = await ctx.client.get<Issue[]>(`/repos/${owner}/${repo}/issues?state=open&limit=100`); const closedIssues = await ctx.client.get<Issue[]>(`/repos/${owner}/${repo}/issues?state=closed&limit=50`); // 统计 const byStatus: Record<string, number> = {}; const byPriority: Record<string, number> = {}; const byType: Record<string, number> = {}; let totalAgeDays = 0; let blockedCount = 0; for (const issue of openIssues) { // 状态统计 const status = getIssueStatus(issue) || 'unknown'; byStatus[status] = (byStatus[status] || 0) + 1; // 优先级统计 const priority = getIssuePriority(issue) || 'unknown'; byPriority[priority] = (byPriority[priority] || 0) + 1; // 类型统计 const prefixes = getLabelPrefixes(config); const typeLabel = issue.labels.find((l) => matchLabel(prefixes.type, l.name) !== null); const type = typeLabel ? (matchLabel(prefixes.type, typeLabel.name) || 'unknown') : 'unknown'; byType[type] = (byType[type] || 0) + 1; // 年龄统计 totalAgeDays += calculateIssueAgeDays(issue); // 阻塞统计 const blockedLabel = buildLabel(prefixes.workflow, 'blocked'); if (hasLabel(issue, blockedLabel)) { blockedCount++; } } const avgAgeDays = openIssues.length > 0 ? Math.round(totalAgeDays / openIssues.length) : 0; // 计算健康度评分 (0-100) let healthScore = 100; if (blockedCount > 0) healthScore -= blockedCount * 5; if (avgAgeDays > 30) healthScore -= 20; else if (avgAgeDays > 14) healthScore -= 10; if (byPriority['P0'] && byPriority['P0'] > 0) healthScore -= byPriority['P0'] * 10; healthScore = Math.max(0, Math.min(100, healthScore)); // 生成建议 const recommendations: string[] = []; if (blockedCount > 0) { recommendations.push(`有 ${blockedCount} 个 Issue 被阻塞,请及时处理`); } if (byPriority['P0'] && byPriority['P0'] > 0) { recommendations.push(`有 ${byPriority['P0']} 个紧急 Issue (P0),需要立即关注`); } if (avgAgeDays > 30) { recommendations.push(`Issue 平均年龄为 ${avgAgeDays} 天,建议加快处理速度`); } if (byStatus['unknown'] && byStatus['unknown'] > 0) { recommendations.push(`有 ${byStatus['unknown']} 个 Issue 缺少状态标签`); } // 生成 Markdown 报告 const markdownReport = generateMarkdownReport( owner, repo, { total_open: openIssues.length, total_closed: closedIssues.length, by_status: byStatus, by_priority: byPriority, by_type: byType, }, blockedCount, avgAgeDays, healthScore, recommendations ); logger.info({ owner, repo, open: openIssues.length, health_score: healthScore }, 'Report generated'); return { success: true, report: { summary: { total_open: openIssues.length, total_closed: closedIssues.length, by_status: byStatus, by_priority: byPriority, by_type: byType, }, blocked_count: blockedCount, avg_age_days: avgAgeDays, health_score: healthScore, recommendations, }, markdown_report: markdownReport, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error({ owner, repo, error: errorMessage }, 'Failed to generate report'); return { success: false, report: { summary: { total_open: 0, total_closed: 0, by_status: {}, by_priority: {}, by_type: {} }, blocked_count: 0, avg_age_days: 0, health_score: 0, recommendations: [], }, markdown_report: '', error: errorMessage, }; } }
  • Registers the 'gitea_workflow_generate_report' tool with MCP server, including title, description, Zod input schema (owner, repo, time_range), and wrapper handler that calls the core workflowGenerateReport function.
    mcpServer.registerTool( 'gitea_workflow_generate_report', { title: '生成工作流报告', description: 'Generate a comprehensive workflow report including issue statistics, health score, and recommendations. Output in JSON and Markdown formats.', inputSchema: z.object({ owner: z.string().optional().describe('Repository owner. Uses context if not provided'), repo: z.string().optional().describe('Repository name. Uses context if not provided'), time_range: z .enum(['day', 'week', 'month']) .optional() .describe('Time range for statistics (default: all time)'), }), }, async (args) => { try { const result = await WorkflowTools.workflowGenerateReport( { client: ctx.client, contextManager: ctx.contextManager }, args ); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], isError: !result.success, }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text' as const, text: `Error: ${errorMessage}` }], isError: true, }; } }
  • Zod input schema definition for the tool, specifying optional owner/repo (resolved from context) and time_range enum.
    { title: '生成工作流报告', description: 'Generate a comprehensive workflow report including issue statistics, health score, and recommendations. Output in JSON and Markdown formats.', inputSchema: z.object({ owner: z.string().optional().describe('Repository owner. Uses context if not provided'), repo: z.string().optional().describe('Repository name. Uses context if not provided'), time_range: z .enum(['day', 'week', 'month']) .optional() .describe('Time range for statistics (default: all time)'), }), },
  • Helper function to generate the Markdown-formatted workflow report from computed statistics and recommendations.
    function generateMarkdownReport( owner: string, repo: string, summary: { total_open: number; total_closed: number; by_status: Record<string, number>; by_priority: Record<string, number>; by_type: Record<string, number>; }, blockedCount: number, avgAgeDays: number, healthScore: number, recommendations: string[] ): string { const lines: string[] = [ `# ${owner}/${repo} 工作流报告`, '', `生成时间: ${new Date().toISOString()}`, '', '## 概览', '', `| 指标 | 值 |`, `|------|-----|`, `| 开放 Issue | ${summary.total_open} |`, `| 已关闭 Issue | ${summary.total_closed} |`, `| 阻塞 Issue | ${blockedCount} |`, `| 平均年龄 | ${avgAgeDays} 天 |`, `| 健康度评分 | ${healthScore}/100 |`, '', '## 状态分布', '', '| 状态 | 数量 |', '|------|------|', ]; for (const [status, count] of Object.entries(summary.by_status)) { lines.push(`| ${status} | ${count} |`); } lines.push(''); lines.push('## 优先级分布'); lines.push(''); lines.push('| 优先级 | 数量 |'); lines.push('|--------|------|'); for (const [priority, count] of Object.entries(summary.by_priority)) { lines.push(`| ${priority} | ${count} |`); } lines.push(''); lines.push('## 类型分布'); lines.push(''); lines.push('| 类型 | 数量 |'); lines.push('|------|------|'); for (const [type, count] of Object.entries(summary.by_type)) { lines.push(`| ${type} | ${count} |`); } if (recommendations.length > 0) { lines.push(''); lines.push('## 建议'); lines.push(''); for (const rec of recommendations) { lines.push(`- ${rec}`); } } return lines.join('\n'); }

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/SupenBysz/gitea-mcp-tool'

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