Skip to main content
Glama
pull-request.ts9.68 kB
/** * Pull Request Management Tools * * PR 管理相关的 MCP 工具实现 */ import type { GiteaClient } from '../gitea-client.js'; import type { ContextManager } from '../context-manager.js'; import type { GiteaPullRequest, CreatePullRequestOptions, UpdatePullRequestOptions, MergePullRequestOptions, PullRequestListOptions, GiteaComment, CreateCommentOptions, } from '../types/gitea.js'; import { createLogger } from '../logger.js'; const logger = createLogger('tools:pull-request'); export interface PullRequestToolsContext { client: GiteaClient; contextManager: ContextManager; } /** * 创建 Pull Request */ export async function createPullRequest( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; title: string; head: string; base: string; body?: string; assignee?: string; assignees?: string[]; milestone?: number; labels?: number[]; due_date?: string; token?: string; } ) { logger.debug({ args }, 'Creating pull request'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const createOptions: CreatePullRequestOptions = { title: args.title, head: args.head, base: args.base, body: args.body, assignee: args.assignee, assignees: args.assignees, milestone: args.milestone, labels: args.labels, due_date: args.due_date, }; const pr = await ctx.client.post<GiteaPullRequest>( `/repos/${owner}/${repo}/pulls`, createOptions, args.token ); logger.info({ owner, repo, pr: pr.number }, 'Pull request created successfully'); return { success: true, pull_request: { id: pr.id, number: pr.number, title: pr.title, body: pr.body, state: pr.state, user: { id: pr.user.id, login: pr.user.login, }, head: { ref: pr.head.ref, sha: pr.head.sha, }, base: { ref: pr.base.ref, sha: pr.base.sha, }, mergeable: pr.mergeable, merged: pr.merged, labels: pr.labels.map((l) => ({ id: l.id, name: l.name, color: l.color })), assignees: pr.assignees?.map((a) => ({ id: a.id, login: a.login })), milestone: pr.milestone ? { id: pr.milestone.id, title: pr.milestone.title } : null, html_url: pr.html_url, diff_url: pr.diff_url, patch_url: pr.patch_url, created_at: pr.created_at, updated_at: pr.updated_at, }, }; } /** * 获取 Pull Request 详情 */ export async function getPullRequest( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; index: number; token?: string; } ) { logger.debug({ args }, 'Getting pull request'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const pr = await ctx.client.get<GiteaPullRequest>( `/repos/${owner}/${repo}/pulls/${args.index}`, undefined, args.token ); logger.debug({ owner, repo, pr: pr.number }, 'Pull request retrieved'); return { success: true, pull_request: { id: pr.id, number: pr.number, title: pr.title, body: pr.body, state: pr.state, user: { id: pr.user.id, login: pr.user.login, full_name: pr.user.full_name, }, head: { label: pr.head.label, ref: pr.head.ref, sha: pr.head.sha, }, base: { label: pr.base.label, ref: pr.base.ref, sha: pr.base.sha, }, mergeable: pr.mergeable, merged: pr.merged, merged_at: pr.merged_at, merged_by: pr.merged_by ? { id: pr.merged_by.id, login: pr.merged_by.login, } : null, labels: pr.labels.map((l) => ({ id: l.id, name: l.name, color: l.color, description: l.description, })), assignees: pr.assignees?.map((a) => ({ id: a.id, login: a.login, full_name: a.full_name, })), milestone: pr.milestone ? { id: pr.milestone.id, title: pr.milestone.title, state: pr.milestone.state, due_on: pr.milestone.due_on, } : null, comments: pr.comments, html_url: pr.html_url, diff_url: pr.diff_url, patch_url: pr.patch_url, created_at: pr.created_at, updated_at: pr.updated_at, closed_at: pr.closed_at, }, }; } /** * 列出 Pull Requests */ export async function listPullRequests( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; state?: 'open' | 'closed' | 'all'; sort?: 'oldest' | 'recentupdate' | 'leastupdate' | 'mostcomment' | 'leastcomment' | 'priority'; page?: number; limit?: number; token?: string; } ) { logger.debug({ args }, 'Listing pull requests'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const listOptions: PullRequestListOptions = { state: args.state || 'open', sort: args.sort, page: args.page || 1, limit: args.limit || 30, }; const prs = await ctx.client.get<GiteaPullRequest[]>( `/repos/${owner}/${repo}/pulls`, listOptions as any, args.token ); logger.debug({ count: prs.length }, 'Pull requests listed'); return { success: true, pull_requests: prs.map((pr) => ({ id: pr.id, number: pr.number, title: pr.title, state: pr.state, user: { id: pr.user.id, login: pr.user.login, }, head: { ref: pr.head.ref, sha: pr.head.sha, }, base: { ref: pr.base.ref, sha: pr.base.sha, }, mergeable: pr.mergeable, merged: pr.merged, labels: pr.labels.map((l) => ({ id: l.id, name: l.name })), assignees: pr.assignees?.map((a) => ({ id: a.id, login: a.login })), comments: pr.comments, html_url: pr.html_url, created_at: pr.created_at, updated_at: pr.updated_at, })), pagination: { page: listOptions.page, limit: listOptions.limit, total: prs.length, }, }; } /** * 更新 Pull Request */ export async function updatePullRequest( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; index: number; title?: string; body?: string; assignee?: string; assignees?: string[]; milestone?: number; state?: 'open' | 'closed'; due_date?: string; unset_due_date?: boolean; token?: string; } ) { logger.debug({ args }, 'Updating pull request'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const updateOptions: UpdatePullRequestOptions = { title: args.title, body: args.body, assignee: args.assignee, assignees: args.assignees, milestone: args.milestone, state: args.state, due_date: args.due_date, unset_due_date: args.unset_due_date, }; const pr = await ctx.client.patch<GiteaPullRequest>( `/repos/${owner}/${repo}/pulls/${args.index}`, updateOptions, args.token ); logger.info({ owner, repo, pr: pr.number }, 'Pull request updated successfully'); return { success: true, pull_request: { id: pr.id, number: pr.number, title: pr.title, body: pr.body, state: pr.state, labels: pr.labels.map((l) => ({ id: l.id, name: l.name })), assignees: pr.assignees?.map((a) => ({ id: a.id, login: a.login })), html_url: pr.html_url, updated_at: pr.updated_at, }, }; } /** * 合并 Pull Request */ export async function mergePullRequest( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; index: number; merge_method?: 'merge' | 'rebase' | 'rebase-merge' | 'squash'; merge_title?: string; merge_message?: string; delete_branch_after_merge?: boolean; token?: string; } ) { logger.debug({ args }, 'Merging pull request'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const mergeOptions: MergePullRequestOptions = { Do: args.merge_method || 'merge', MergeTitleField: args.merge_title, MergeMessageField: args.merge_message, delete_branch_after_merge: args.delete_branch_after_merge, }; await ctx.client.post( `/repos/${owner}/${repo}/pulls/${args.index}/merge`, mergeOptions, args.token ); logger.info({ owner, repo, pr: args.index }, 'Pull request merged successfully'); return { success: true, message: `Pull request #${args.index} has been merged`, }; } /** * 审查 Pull Request (添加审查评论) */ export async function reviewPullRequest( ctx: PullRequestToolsContext, args: { owner?: string; repo?: string; index: number; body: string; token?: string; } ) { logger.debug({ args }, 'Reviewing pull request'); const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo); const commentOptions: CreateCommentOptions = { body: args.body, }; const comment = await ctx.client.post<GiteaComment>( `/repos/${owner}/${repo}/issues/${args.index}/comments`, commentOptions, args.token ); logger.info({ owner, repo, pr: args.index }, 'Review comment added successfully'); return { success: true, comment: { id: comment.id, body: comment.body, user: { id: comment.user.id, login: comment.user.login, }, html_url: comment.html_url, created_at: comment.created_at, updated_at: comment.updated_at, }, }; }

Implementation Reference

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