diff --git a/src/github/index.ts b/src/github/index.ts index 800bce83..06cdc7d4 100644 --- a/src/github/index.ts +++ b/src/github/index.ts @@ -41,7 +41,8 @@ import { CreateIssueSchema, CreatePullRequestSchema, ForkRepositorySchema, - CreateBranchSchema + CreateBranchSchema, + ListCommitsSchema } from './schemas.js'; import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; @@ -467,6 +468,40 @@ async function createRepository( return GitHubRepositorySchema.parse(await response.json()); } +async function listCommits( + owner: string, + repo: string, + page: number = 1, + perPage: number = 30, + sha?: string, +): Promise { + const url = new URL(`https://api.github.com/repos/${owner}/${repo}/commits`); + url.searchParams.append("page", page.toString()); + url.searchParams.append("per_page", perPage.toString()); + if (sha) { + url.searchParams.append("sha", sha); + } + + const response = await fetch( + url.toString(), + { + method: "GET", + headers: { + "Authorization": `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`, + "Accept": "application/vnd.github.v3+json", + "User-Agent": "github-mcp-server", + "Content-Type": "application/json" + }, + } + ); + + if (!response.ok) { + throw new Error(`GitHub API error: ${response.statusText}`); + } + + return GitHubCommitSchema.array().parse(await response.json()); +} + server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ @@ -514,6 +549,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { name: "create_branch", description: "Create a new branch in a GitHub repository", inputSchema: zodToJsonSchema(CreateBranchSchema) + }, + { + name: "list_commits", + description: "Get list of commits of a branch in a GitHub repository", + inputSchema: zodToJsonSchema(ListCommitsSchema) } ] }; @@ -623,6 +663,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { return { content: [{ type: "text", text: JSON.stringify(pullRequest, null, 2) }] }; } + case "list_commits": { + const args = ListCommitsSchema.parse(request.params.arguments); + const results = await listCommits(args.owner, args.repo, args.page, args.perPage, args.sha); + return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] }; + } + default: throw new Error(`Unknown tool: ${request.params.name}`); } diff --git a/src/github/schemas.ts b/src/github/schemas.ts index 213458eb..defc2569 100644 --- a/src/github/schemas.ts +++ b/src/github/schemas.ts @@ -308,6 +308,15 @@ export const SearchRepositoriesSchema = z.object({ perPage: z.number().optional().describe("Number of results per page (default: 30, max: 100)") }); +export const ListCommitsSchema = z.object({ + owner: z.string().describe("Repository owner (username or organization)"), + repo: z.string().describe("Repository name"), + page: z.number().optional().describe("Page number for pagination (default: 1)"), + perPage: z.number().optional().describe("Number of results per page (default: 30, max: 100)"), + sha: z.string().optional() + .describe("SHA of the file being replaced (required when updating existing files)") +}); + export const CreateRepositorySchema = z.object({ name: z.string().describe("Repository name"), description: z.string().optional().describe("Repository description"),