Skip to main content
Glama

githubSearchPullRequests

Read-onlyIdempotent

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
NameRequiredDescriptionDefault
queriesYesResearch 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
    );
  • 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(
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true, destructiveHint=false, openWorldHint=true, and idempotentHint=true. The description adds valuable context beyond annotations: warnings about token-heavy fullContent fetches, guidance on optimal query limits (1-3 queries per call), and workflow recommendations. No contradiction with annotations exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description uses a well-structured template with clear sections (<when>, <fromTool>, <gotchas>, <examples>) that makes information easy to find. Every sentence serves a purpose - providing use cases, warnings, examples, and workflow guidance without unnecessary verbosity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complex input schema with 1 parameter containing 35+ nested properties, the description provides excellent contextual guidance about when and how to use the tool effectively. The lack of output schema is compensated by clear examples showing expected parameter combinations. The main gap is no explicit mention of rate limits or authentication requirements.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds some semantic context through examples showing how to use prNumber, type, owner, repo, state, merged, limit, and partialContentMetadata parameters, but doesn't significantly enhance understanding beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches or fetches pull requests with metadata, diffs, and discussions. It distinguishes from siblings like githubSearchCode (code search) and githubSearchRepositories (repo search) by focusing specifically on pull requests. The title 'GitHub Pull Request Search' reinforces this specificity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The <when> section explicitly lists use cases like implementation history, review changes, and workflow guidance. The <gotchas> section provides clear warnings about avoiding large changes and prNumber behavior. It also mentions self-refinement strategies and alternatives like 'Find changes or known issues in repository' from other tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/bgauryy/octocode-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server