githubSearchPullRequests
Search and retrieve GitHub pull requests to review changes, analyze implementation history, and examine code diffs and discussions.
Instructions
Search or fetch Pull Requests (metadata, diffs, discussions)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| queries | Yes | Research queries for githubSearchPullRequests (1-3 queries per call for optimal resource management). Review schema before use for optimal results |
Implementation Reference
- Core execution logic for the githubSearchPullRequests tool. Handles bulk queries, validation errors, API calls to GitHub, result processing, pagination hints, and error handling.async function searchMultipleGitHubPullRequests( queries: GitHubPullRequestSearchQuery[], authInfo?: AuthInfo, sessionId?: string ): Promise<CallToolResult> { return executeBulkOperation( queries, async (query: GitHubPullRequestSearchQuery, _index: number) => { try { const validationError = (query as unknown as Record<string, unknown>) ?._validationError; if (validationError && typeof validationError === 'string') { return createErrorResult(validationError, query); } const apiResult = await searchGitHubPullRequestsAPI( query, authInfo, sessionId ); const apiError = handleApiError(apiResult, query); if (apiError) return apiError; const pullRequests = apiResult.pull_requests || []; const hasContent = pullRequests.length > 0; // Generate pagination hints const paginationHints: string[] = []; if (apiResult.pagination) { const { currentPage, totalPages, totalMatches, hasMore } = apiResult.pagination; paginationHints.push( `Page ${currentPage}/${totalPages} (showing ${pullRequests.length} of ${totalMatches} PRs)` ); if (hasMore) { paginationHints.push(`Next: page=${currentPage + 1}`); } if (currentPage > 1) { paginationHints.push(`Previous: page=${currentPage - 1}`); } if (!hasMore) { paginationHints.push('Final page'); } if (totalPages > 2) { paginationHints.push( `Jump to: page=1 (first) or page=${totalPages} (last)` ); } } // Use unified pattern: context for dynamic hints, extraHints for pagination return createSuccessResult( query, { owner: query.owner, repo: query.repo, pull_requests: pullRequests, total_count: apiResult.total_count || pullRequests.length, ...(apiResult.pagination && { pagination: apiResult.pagination }), }, hasContent, TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, { hintContext: { matchCount: pullRequests.length }, extraHints: paginationHints, } ); } catch (error) { return handleCatchError(error, query); } }, { toolName: TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, keysPriority: [ 'owner', 'repo', 'pull_requests', 'pagination', 'total_count', 'error', ] satisfies Array<keyof PullRequestSearchResult>, } ); }
- Registers the tool with the MCP server, specifying name, description, input schema, annotations, and the wrapped handler function.export function registerSearchGitHubPullRequestsTool( server: McpServer, callback?: ToolInvocationCallback ) { return server.registerTool( TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, { description: DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS], inputSchema: GitHubPullRequestSearchBulkQuerySchema, annotations: { title: 'GitHub Pull Request Search', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, withSecurityValidation( TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, async ( args: { queries: GitHubPullRequestSearchQuery[]; }, authInfo, sessionId ): Promise<CallToolResult> => { let queries = args.queries || []; await invokeCallbackSafely( callback, TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, queries ); const longQueryIndex = queries.findIndex(hasQueryLengthError); if (longQueryIndex !== -1) { queries = queries.map((q, i) => i === longQueryIndex ? addValidationError(q, VALIDATION_MESSAGES.QUERY_TOO_LONG) : q ); } if (queries.length > 0 && !queries.some(hasValidSearchParams)) { queries = queries.map((q, i) => i === 0 ? addValidationError(q, VALIDATION_MESSAGES.MISSING_PARAMS) : q ); } return searchMultipleGitHubPullRequests(queries, authInfo, sessionId); } ) ); }
- Zod schema defining the input parameters for GitHub pull request search queries, including search terms, scopes, filters, sorting, pagination, and output options.export const GitHubPullRequestSearchQuerySchema = BaseQuerySchema.extend({ query: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.search.query), owner: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.scope.owner), repo: z.string().optional().describe(GITHUB_SEARCH_PULL_REQUESTS.scope.repo), prNumber: z .number() .int() .positive() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.scope.prNumber), state: z .enum(['open', 'closed']) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.state), assignee: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.assignee), author: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.author), commenter: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.commenter), involves: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.involves), mentions: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.mentions), 'review-requested': z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['review-requested']), 'reviewed-by': z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['reviewed-by']), label: z .union([z.string(), z.array(z.string())]) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.label), 'no-label': z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['no-label']), 'no-milestone': z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['no-milestone']), 'no-project': z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['no-project']), 'no-assignee': z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['no-assignee']), head: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.head), base: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.base), created: DateRangeSchema.shape.created, updated: DateRangeSchema.shape.updated, closed: z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.closed), 'merged-at': z .string() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters['merged-at']), comments: z .union([ z.number().int().min(0), z.string().regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/), ]) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.comments), reactions: z .union([ z.number().int().min(0), z.string().regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/), ]) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.reactions), interactions: z .union([ z.number().int().min(0), z.string().regex(/^(>=?\d+|<=?\d+|\d+\.\.\d+|\d+)$/), ]) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.interactions), merged: z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.merged), draft: z .boolean() .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.filters.draft), match: PRMatchScopeSchema, sort: z .enum(['created', 'updated', 'best-match']) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.sorting.sort), order: z .enum(['asc', 'desc']) .optional() .default('desc') .describe(GITHUB_SEARCH_PULL_REQUESTS.sorting.order), limit: z .number() .min(1) .max(10) .default(5) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.resultLimit.limit), page: z .number() .int() .min(1) .max(10) .default(1) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.pagination.page), withComments: z .boolean() .default(false) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.outputShaping.withComments), withCommits: z .boolean() .default(false) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.outputShaping.withCommits), type: z .enum(['metadata', 'fullContent', 'partialContent']) .default('metadata') .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.outputShaping.type), partialContentMetadata: z .array( z.object({ file: z.string(), additions: z.array(z.number()).optional(), deletions: z.array(z.number()).optional(), }) ) .optional() .describe(GITHUB_SEARCH_PULL_REQUESTS.outputShaping.partialContentMetadata), }); export const GitHubPullRequestSearchBulkQuerySchema = createBulkQuerySchema( TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, GitHubPullRequestSearchQuerySchema );
- packages/octocode-mcp/src/tools/toolConfig.ts:72-79 (registration)Tool configuration entry that references the registration function and metadata for githubSearchPullRequests.export const GITHUB_SEARCH_PULL_REQUESTS: ToolConfig = { name: TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS, description: getDescription(TOOL_NAMES.GITHUB_SEARCH_PULL_REQUESTS), isDefault: true, isLocal: false, type: 'history', fn: registerSearchGitHubPullRequestsTool, };
- Low-level API function that performs the actual GitHub GraphQL/REST search for pull requests based on the query.export async function searchGitHubPullRequestsAPI( params: GitHubPullRequestsSearchParams, authInfo?: AuthInfo, sessionId?: string ): Promise<PullRequestSearchResult> { // Cache key excludes context fields (mainResearchGoal, researchGoal, reasoning) // as they don't affect the API response const cacheKey = generateCacheKey( 'gh-api-prs', { query: params.query, owner: params.owner, repo: params.repo, prNumber: params.prNumber, state: params.state, draft: params.draft, merged: params.merged, author: params.author, assignee: params.assignee, mentions: params.mentions, commenter: params.commenter, involves: params.involves, 'reviewed-by': params['reviewed-by'], 'review-requested': params['review-requested'], head: params.head, base: params.base, created: params.created, updated: params.updated, 'merged-at': params['merged-at'], closed: params.closed, comments: params.comments, reactions: params.reactions, interactions: params.interactions, label: params.label, 'no-assignee': params['no-assignee'], 'no-label': params['no-label'], 'no-milestone': params['no-milestone'], 'no-project': params['no-project'], match: params.match, sort: params.sort, order: params.order, limit: params.limit, page: params.page, withComments: params.withComments, withCommits: params.withCommits, type: params.type, partialContentMetadata: params.partialContentMetadata, }, sessionId ); const result = await withDataCache<PullRequestSearchResult>( cacheKey, async () => { return await searchGitHubPullRequestsAPIInternal( params, authInfo, sessionId ); }, { shouldCache: (value: PullRequestSearchResult) => !value.error, } ); return result; } async function searchGitHubPullRequestsAPIInternal(