UNPKG

github-repos-manager-mcp

Version:

Model Context Protocol (MCP) server that enables your MCP client (e.g., Claude Desktop, Cline, Roo Code, Cursor, Windsurf, etc.) to interact with GitHub repositories using your GitHub personal access token.

252 lines (224 loc) 6.32 kB
// src/formatters/branches-commits.cjs function formatListBranchesOutput(branches, owner, repo) { if (!Array.isArray(branches)) { return { content: [ { type: "text", text: `Error: Could not retrieve branches for ${owner}/${repo}.`, }, ], isError: true, }; } if (branches.length === 0) { return { content: [ { type: "text", text: `No branches found in ${owner}/${repo}`, }, ], }; } const formatted = branches .map( (branch) => `**${branch.name}**\n` + `Protected: ${branch.protected ? "Yes" : "No"}\n` + `Commit SHA: ${branch.commit.sha}\n` + // `Commit Message: ${branch.commit.commit.message.split("\n")[0]}\n` + // commit.commit.message might not exist on branch list // `Author: ${branch.commit.commit.author.name}\n` + // commit.commit.author might not exist // `Date: ${new Date(branch.commit.commit.author.date).toLocaleDateString()}` `Last commit URL: ${branch.commit.url}` // Simpler, more reliable info from branch list ) .join("\n\n"); return { content: [ { type: "text", text: `Found ${branches.length} branches in ${owner}/${repo}:\n\n${formatted}`, }, ], }; } function formatCreateBranchOutput( newBranch, branch_name, owner, repo, source_sha, source_branch ) { if (!newBranch || typeof newBranch !== "object" || !newBranch.ref) { return { content: [ { type: "text", text: `Error: Could not create branch "${branch_name}" in ${owner}/${repo}.`, }, ], isError: true, }; } return { content: [ { type: "text", text: `Created branch "${branch_name}" in ${owner}/${repo}:\n\n` + `Ref: ${newBranch.ref}\n` + `SHA: ${newBranch.object.sha}\n` + `Source: ${ source_sha ? `SHA ${source_sha}` : `branch ${source_branch}` }\n` + `URL: ${newBranch.url}`, // This URL is for the ref itself, not the branch page. }, ], }; } function formatListCommitsOutput(commits, owner, repo) { if (!Array.isArray(commits)) { return { content: [ { type: "text", text: `Error: Could not retrieve commits for ${owner}/${repo}.`, }, ], isError: true, }; } if (commits.length === 0) { return { content: [ { type: "text", text: `No commits found in ${owner}/${repo} with the specified filters`, }, ], }; } const formatted = commits .map( (commit) => `**${commit.sha.substring(0, 7)}** ${ commit.commit.message.split("\n")[0] }\n` + `Author: ${commit.commit.author.name} <${commit.commit.author.email}>\n` + `Date: ${new Date(commit.commit.author.date).toLocaleDateString()}\n` + // `Files changed: ${commit.files ? commit.files.length : "N/A"}\n` + // 'files' is not part of list commits response `URL: ${commit.html_url}` ) .join("\n\n"); return { content: [ { type: "text", text: `Found ${commits.length} commits in ${owner}/${repo}:\n\n${formatted}`, }, ], }; } function formatGetCommitDetailsOutput(commit) { if (!commit || typeof commit !== "object" || !commit.sha) { return { content: [ { type: "text", text: "Error: Could not retrieve commit details." }, ], isError: true, }; } const stats = commit.stats || {}; const files = commit.files || []; const fileChanges = files .map( (file) => `**${file.filename}** (${file.status})\n` + `+${file.additions} -${file.deletions} changes: ${file.changes}` ) .join("\n"); const info = ` **Commit Details for ${commit.sha.substring(0, 7)}** **Message:** ${commit.commit.message} **Author:** ${commit.commit.author.name} <${commit.commit.author.email}> **Committer:** ${commit.commit.committer.name} <${ commit.commit.committer.email }> **Date:** ${new Date(commit.commit.author.date).toLocaleString()} **Statistics:** - Total files changed: ${stats.total || 0} - Additions: +${stats.additions || 0} - Deletions: -${stats.deletions || 0} **Parents:** ${commit.parents.map((p) => p.sha.substring(0, 7)).join(", ")} **File Changes:** ${fileChanges || "No file information available"} **URL:** ${commit.html_url} `.trim(); return { content: [ { type: "text", text: info, }, ], }; } function formatCompareCommitsOutput(comparison, base, head) { if (!comparison || typeof comparison !== "object" || !comparison.status) { return { content: [ { type: "text", text: `Error: Could not compare ${base}...${head}.` }, ], isError: true, }; } const stats = comparison.stats || {}; // stats might not exist on comparison object directly const files = comparison.files || []; const fileChanges = files .slice(0, 20) // Limit to first 20 files to avoid overwhelming output .map( (file) => `**${file.filename}** (${file.status})\n` + `+${file.additions} -${file.deletions} (${file.changes} changes)` ) .join("\n"); const info = ` **Comparison: ${base}...${head}** **Status:** ${comparison.status} **Ahead by:** ${comparison.ahead_by} commits **Behind by:** ${comparison.behind_by} commits **Total commits:** ${comparison.total_commits} **Merge Base Commit:** ${comparison.merge_base_commit.sha.substring(0, 7)} **Recent Commits in Range (up to 5):** ${comparison.commits .slice(0, 5) .map( (commit) => `• ${commit.sha.substring(0, 7)} ${ commit.commit.message.split("\n")[0] } (${commit.commit.author.name})` ) .join("\n")} **File Changes${ files.length > 20 ? ` (showing first 20 of ${files.length})` : "" }:** ${fileChanges || "No file changes"} **URL:** ${comparison.html_url} `.trim(); return { content: [ { type: "text", text: info, }, ], }; } module.exports = { formatListBranchesOutput, formatCreateBranchOutput, formatListCommitsOutput, formatGetCommitDetailsOutput, formatCompareCommitsOutput, };