Add reading PR files and status, merging PRs
This commit is contained in:
parent
9e25ffd599
commit
353fbb8d0a
3 changed files with 220 additions and 0 deletions
|
|
@ -225,6 +225,33 @@ MCP Server for the GitHub API, enabling file operations, repository management,
|
||||||
- `body` (string): Comment text
|
- `body` (string): Comment text
|
||||||
- Returns: Created review details
|
- Returns: Created review details
|
||||||
|
|
||||||
|
21. `merge_pull_request`
|
||||||
|
- Merge a pull request
|
||||||
|
- Inputs:
|
||||||
|
- `owner` (string): Repository owner
|
||||||
|
- `repo` (string): Repository name
|
||||||
|
- `pull_number` (number): Pull request number
|
||||||
|
- `commit_title` (optional string): Title for merge commit
|
||||||
|
- `commit_message` (optional string): Extra detail for merge commit
|
||||||
|
- `merge_method` (optional string): Merge method ('merge', 'squash', 'rebase')
|
||||||
|
- Returns: Merge result details
|
||||||
|
|
||||||
|
22. `get_pull_request_files`
|
||||||
|
- Get the list of files changed in a pull request
|
||||||
|
- Inputs:
|
||||||
|
- `owner` (string): Repository owner
|
||||||
|
- `repo` (string): Repository name
|
||||||
|
- `pull_number` (number): Pull request number
|
||||||
|
- Returns: Array of changed files with patch and status details
|
||||||
|
|
||||||
|
23. `get_pull_request_status`
|
||||||
|
- Get the combined status of all status checks for a pull request
|
||||||
|
- Inputs:
|
||||||
|
- `owner` (string): Repository owner
|
||||||
|
- `repo` (string): Repository name
|
||||||
|
- `pull_number` (number): Pull request number
|
||||||
|
- Returns: Combined status check results and individual check details
|
||||||
|
|
||||||
## Search Query Syntax
|
## Search Query Syntax
|
||||||
|
|
||||||
### Code Search
|
### Code Search
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ import {
|
||||||
GetFileContentsSchema,
|
GetFileContentsSchema,
|
||||||
GetIssueSchema,
|
GetIssueSchema,
|
||||||
GetPullRequestSchema,
|
GetPullRequestSchema,
|
||||||
|
GetPullRequestFilesSchema,
|
||||||
|
GetPullRequestStatusSchema,
|
||||||
|
MergePullRequestSchema,
|
||||||
GitHubCommitSchema,
|
GitHubCommitSchema,
|
||||||
GitHubContentSchema,
|
GitHubContentSchema,
|
||||||
GitHubCreateUpdateFileResponseSchema,
|
GitHubCreateUpdateFileResponseSchema,
|
||||||
|
|
@ -40,6 +43,8 @@ import {
|
||||||
ListPullRequestsSchema,
|
ListPullRequestsSchema,
|
||||||
CreatePullRequestReviewSchema,
|
CreatePullRequestReviewSchema,
|
||||||
PushFilesSchema,
|
PushFilesSchema,
|
||||||
|
PullRequestFileSchema,
|
||||||
|
CombinedStatusSchema,
|
||||||
SearchCodeResponseSchema,
|
SearchCodeResponseSchema,
|
||||||
SearchCodeSchema,
|
SearchCodeSchema,
|
||||||
SearchIssuesResponseSchema,
|
SearchIssuesResponseSchema,
|
||||||
|
|
@ -798,6 +803,99 @@ async function createPullRequestReview(
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function mergePullRequest(
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
pullNumber: number,
|
||||||
|
options: Omit<z.infer<typeof MergePullRequestSchema>, 'owner' | 'repo' | 'pull_number'>
|
||||||
|
): Promise<any> {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/merge`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "github-mcp-server",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(options),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`GitHub API error: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPullRequestFiles(
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
pullNumber: number
|
||||||
|
): Promise<z.infer<typeof PullRequestFileSchema>[]> {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}/files`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "github-mcp-server",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`GitHub API error: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return z.array(PullRequestFileSchema).parse(await response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPullRequestStatus(
|
||||||
|
owner: string,
|
||||||
|
repo: string,
|
||||||
|
pullNumber: number
|
||||||
|
): Promise<z.infer<typeof CombinedStatusSchema>> {
|
||||||
|
// First get the PR to get the head SHA
|
||||||
|
const prResponse = await fetch(
|
||||||
|
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "github-mcp-server",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!prResponse.ok) {
|
||||||
|
throw new Error(`GitHub API error: ${prResponse.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pr = GitHubPullRequestSchema.parse(await prResponse.json());
|
||||||
|
const sha = pr.head.sha;
|
||||||
|
|
||||||
|
// Then get the combined status for that SHA
|
||||||
|
const statusResponse = await fetch(
|
||||||
|
`https://api.github.com/repos/${owner}/${repo}/commits/${sha}/status`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${GITHUB_PERSONAL_ACCESS_TOKEN}`,
|
||||||
|
Accept: "application/vnd.github.v3+json",
|
||||||
|
"User-Agent": "github-mcp-server",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!statusResponse.ok) {
|
||||||
|
throw new Error(`GitHub API error: ${statusResponse.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CombinedStatusSchema.parse(await statusResponse.json());
|
||||||
|
}
|
||||||
|
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
return {
|
return {
|
||||||
tools: [
|
tools: [
|
||||||
|
|
@ -904,6 +1002,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||||
name: "create_pull_request_review",
|
name: "create_pull_request_review",
|
||||||
description: "Create a review on a pull request",
|
description: "Create a review on a pull request",
|
||||||
inputSchema: zodToJsonSchema(CreatePullRequestReviewSchema)
|
inputSchema: zodToJsonSchema(CreatePullRequestReviewSchema)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge_pull_request",
|
||||||
|
description: "Merge a pull request",
|
||||||
|
inputSchema: zodToJsonSchema(MergePullRequestSchema)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_pull_request_files",
|
||||||
|
description: "Get the list of files changed in a pull request",
|
||||||
|
inputSchema: zodToJsonSchema(GetPullRequestFilesSchema)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_pull_request_status",
|
||||||
|
description: "Get the combined status of all status checks for a pull request",
|
||||||
|
inputSchema: zodToJsonSchema(GetPullRequestStatusSchema)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
@ -1129,6 +1242,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||||
return { toolResult: review };
|
return { toolResult: review };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "merge_pull_request": {
|
||||||
|
const args = MergePullRequestSchema.parse(request.params.arguments);
|
||||||
|
const { owner, repo, pull_number, ...options } = args;
|
||||||
|
const result = await mergePullRequest(owner, repo, pull_number, options);
|
||||||
|
return { toolResult: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_pull_request_files": {
|
||||||
|
const args = GetPullRequestFilesSchema.parse(request.params.arguments);
|
||||||
|
const files = await getPullRequestFiles(args.owner, args.repo, args.pull_number);
|
||||||
|
return { toolResult: files };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_pull_request_status": {
|
||||||
|
const args = GetPullRequestStatusSchema.parse(request.params.arguments);
|
||||||
|
const status = await getPullRequestStatus(args.owner, args.repo, args.pull_number);
|
||||||
|
return { toolResult: status };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown tool: ${request.params.name}`);
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -752,3 +752,64 @@ export type SearchUsersResponse = z.infer<typeof SearchUsersResponseSchema>;
|
||||||
export type GetPullRequest = z.infer<typeof GetPullRequestSchema>;
|
export type GetPullRequest = z.infer<typeof GetPullRequestSchema>;
|
||||||
export type ListPullRequests = z.infer<typeof ListPullRequestsSchema>;
|
export type ListPullRequests = z.infer<typeof ListPullRequestsSchema>;
|
||||||
export type CreatePullRequestReview = z.infer<typeof CreatePullRequestReviewSchema>;
|
export type CreatePullRequestReview = z.infer<typeof CreatePullRequestReviewSchema>;
|
||||||
|
|
||||||
|
// Schema for merging a pull request
|
||||||
|
export const MergePullRequestSchema = z.object({
|
||||||
|
owner: z.string().describe("Repository owner (username or organization)"),
|
||||||
|
repo: z.string().describe("Repository name"),
|
||||||
|
pull_number: z.number().describe("Pull request number"),
|
||||||
|
commit_title: z.string().optional().describe("Title for the automatic commit message"),
|
||||||
|
commit_message: z.string().optional().describe("Extra detail to append to automatic commit message"),
|
||||||
|
merge_method: z.enum(['merge', 'squash', 'rebase']).optional().describe("Merge method to use")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schema for getting PR files
|
||||||
|
export const GetPullRequestFilesSchema = z.object({
|
||||||
|
owner: z.string().describe("Repository owner (username or organization)"),
|
||||||
|
repo: z.string().describe("Repository name"),
|
||||||
|
pull_number: z.number().describe("Pull request number")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PullRequestFileSchema = z.object({
|
||||||
|
sha: z.string(),
|
||||||
|
filename: z.string(),
|
||||||
|
status: z.enum(['added', 'removed', 'modified', 'renamed', 'copied', 'changed', 'unchanged']),
|
||||||
|
additions: z.number(),
|
||||||
|
deletions: z.number(),
|
||||||
|
changes: z.number(),
|
||||||
|
blob_url: z.string(),
|
||||||
|
raw_url: z.string(),
|
||||||
|
contents_url: z.string(),
|
||||||
|
patch: z.string().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schema for checking PR status
|
||||||
|
export const GetPullRequestStatusSchema = z.object({
|
||||||
|
owner: z.string().describe("Repository owner (username or organization)"),
|
||||||
|
repo: z.string().describe("Repository name"),
|
||||||
|
pull_number: z.number().describe("Pull request number")
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StatusCheckSchema = z.object({
|
||||||
|
url: z.string(),
|
||||||
|
state: z.enum(['error', 'failure', 'pending', 'success']),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
target_url: z.string().nullable(),
|
||||||
|
context: z.string(),
|
||||||
|
created_at: z.string(),
|
||||||
|
updated_at: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CombinedStatusSchema = z.object({
|
||||||
|
state: z.enum(['error', 'failure', 'pending', 'success']),
|
||||||
|
statuses: z.array(StatusCheckSchema),
|
||||||
|
sha: z.string(),
|
||||||
|
total_count: z.number()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type MergePullRequest = z.infer<typeof MergePullRequestSchema>;
|
||||||
|
export type GetPullRequestFiles = z.infer<typeof GetPullRequestFilesSchema>;
|
||||||
|
export type PullRequestFile = z.infer<typeof PullRequestFileSchema>;
|
||||||
|
export type GetPullRequestStatus = z.infer<typeof GetPullRequestStatusSchema>;
|
||||||
|
export type StatusCheck = z.infer<typeof StatusCheckSchema>;
|
||||||
|
export type CombinedStatus = z.infer<typeof CombinedStatusSchema>;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue