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.

152 lines (132 loc) 3.82 kB
// src/handlers/milestones.cjs const milestoneFormatters = require("../formatters/milestones.cjs"); async function listMilestones(args, apiService) { const { owner, repo, state = "open", sort = "due_on", direction = "asc", per_page = 30, } = args; if (!owner || !repo) { throw new Error( "Owner and repository name are required for listMilestones. Please provide them in arguments or ensure a default is set." ); } const params = new URLSearchParams({ state, sort, direction, per_page: per_page.toString(), }); const milestones = await apiService.makeGitHubRequest( `/repos/${owner}/${repo}/milestones?${params.toString()}` ); return milestoneFormatters.formatListMilestonesOutput( milestones, owner, repo, state ); } async function createMilestone(args, apiService) { const { owner, repo, title, state = "open", description, due_on } = args; if (!owner || !repo) { throw new Error( "Owner and repository name are required for createMilestone. Please provide them in arguments or ensure a default is set." ); } if (!title) { throw new Error("Milestone title is required for createMilestone."); } const milestoneData = { title, state, description: description || "", }; if (due_on) { // Ensure due_on is in YYYY-MM-DDTHH:MM:SSZ format if provided milestoneData.due_on = due_on; } const milestone = await apiService.makeGitHubRequest( `/repos/${owner}/${repo}/milestones`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(milestoneData), } ); return milestoneFormatters.formatCreateMilestoneOutput( milestone, owner, repo ); } async function editMilestone(args, apiService) { const { owner, repo, milestone_number, title, state, description, due_on } = args; if (!owner || !repo) { throw new Error( "Owner and repository name are required for editMilestone. Please provide them in arguments or ensure a default is set." ); } if (!milestone_number) { throw new Error("milestone_number is required for editMilestone."); } const payload = {}; if (title !== undefined) payload.title = title; if (state !== undefined) payload.state = state; if (description !== undefined) payload.description = description; if (due_on !== undefined) payload.due_on = due_on; // Ensure due_on is in YYYY-MM-DDTHH:MM:SSZ format or null if (Object.keys(payload).length === 0) { throw new Error( "At least one field to update (title, state, description, due_on) must be provided for editMilestone." ); } const updatedMilestone = await apiService.makeGitHubRequest( `/repos/${owner}/${repo}/milestones/${milestone_number}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), } ); return milestoneFormatters.formatEditMilestoneOutput( updatedMilestone, owner, repo ); } async function deleteMilestone(args, apiService) { const { owner, repo, milestone_number } = args; if (!owner || !repo) { throw new Error( "Owner and repository name are required for deleteMilestone. Please provide them in arguments or ensure a default is set." ); } if (!milestone_number) { throw new Error("milestone_number is required for deleteMilestone."); } await apiService.makeGitHubRequest( `/repos/${owner}/${repo}/milestones/${milestone_number}`, { method: "DELETE", } ); return milestoneFormatters.formatDeleteMilestoneOutput( milestone_number, owner, repo ); } module.exports = { listMilestones, createMilestone, editMilestone, deleteMilestone, };