Skip to main content
Glama
radireddy

GitHub MCP Server

by radireddy

github.getAuthoredPRs

Fetch and analyze pull requests authored by a GitHub user within specified repositories and time periods to assess code contributions, track activity trends, and gather performance metrics.

Instructions

Fetch all pull requests authored by a given user within a time range. Returns PRs with metadata including state (OPEN/MERGED/CLOSED), creation/merge dates, code statistics (files changed, additions, deletions), and repository information. Optionally filter by specific repositories. Automatically filters out auto-generated PRs (e.g., backmerge PRs). Use this tool to analyze a user's code contribution activity.

Example use cases:

  • Assess developer productivity by counting PRs authored in a time period

  • Analyze code contribution trends over time

  • Get detailed metrics for performance reviews

  • Track PR merge rates and code change statistics

Returns: Array of PR objects with id, repo, title, createdAt, mergedAt, state, filesChanged, additions, deletions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
usernameYesGitHub username (case-insensitive, @ prefix optional). Examples: "octocat", "@octocat", "JohnDoe"
reposYesArray of repositories in owner/repo format (required, at least one). Example: ["owner/repo1", "owner/repo2"]
fromNoOptional: Start timestamp in ISO 8601 format. Must be before "to" parameter if provided. If omitted, uses last 3 months. Example: "2024-01-01T00:00:00Z"
toNoOptional: End timestamp in ISO 8601 format. Must be after "from" parameter if provided. If omitted, uses last 3 months. Example: "2024-12-31T23:59:59Z"

Implementation Reference

  • Core handler implementation for the github.getAuthoredPRs tool. Validates inputs, performs GitHub GraphQL search for PR IDs, fetches detailed PR data in batches, applies filters (time range, repository, auto-generated PRs), maps to AuthoredPR format, and returns the results.
    async getAuthoredPRs(
      username: string,
      repos: string[],
      from?: string,
      to?: string
    ): Promise<{ prs: AuthoredPR[] }> {
      // Validate inputs
      const { normalizedUsername, normalizedRepos, from: validatedFrom, to: validatedTo } =
        this.validateCommonParameters(username, repos, from, to);
    
      // Format dates for GitHub search (YYYY-MM-DD format)
      const fromDate = new Date(validatedFrom).toISOString().split('T')[0];
      const toDate = new Date(validatedTo).toISOString().split('T')[0];
    
      // Build search query
      // GitHub search syntax: is:pr created:YYYY-MM-DD..YYYY-MM-DD author:username
      // Note: GitHub search uses inclusive date ranges
      // GitHub search supports multiple repo: filters
      const repoFilters = normalizedRepos.map(repo => `repo:${repo}`).join(' ');
      let searchQuery = `is:pr created:${fromDate}..${toDate} author:${normalizedUsername} ${repoFilters}`;
    
      // Two-step approach: get PR IDs from search, then fetch full details by ID
      // This avoids issues with null nodes in search results for private org repos
    
      // Step 1: Collect all PR IDs from search
      const prIds: string[] = [];
      await fetchAllPages(
        async (cursor: string | null) => {
          const response = await this.client.query(QUERIES.AUTHoredPRsSearch, {
            searchQuery: searchQuery,
            after: cursor,
          });
          return response.data;
        },
        (data: any) => {
          const searchData = data.search || {};
          const nodes = searchData.nodes || [];
    
          // Extract PR IDs (filter out null nodes)
          for (const node of nodes) {
            if (node && node.id && node.__typename === 'PullRequest') {
              prIds.push(node.id);
            }
          }
    
          return []; // Return empty array since we're collecting IDs in closure
        },
        (data: any) => {
          const searchData = data.search || {};
          return extractPageInfo(searchData.pageInfo || {});
        }
      );
    
      if (prIds.length === 0) {
        return { prs: [] };
      }
    
      // Step 2: Fetch full PR details by ID (batch in groups of 100, GitHub's limit)
      const batchSize = 100;
      const allPRs: any[] = [];
    
      for (let i = 0; i < prIds.length; i += batchSize) {
        const batch = prIds.slice(i, i + batchSize);
    
        const response = await this.client.query(QUERIES.AUTHoredPRsDetails, {
          prIds: batch,
        });
    
        const nodes = response.data.nodes || [];
    
        // Filter out null nodes and collect PRs
        for (const node of nodes) {
          if (node && node.id) {
            allPRs.push(node);
          }
        }
      }
    
      // Step 3: Filter and map PRs (apply time range filter and repository filter)
      const fromTime = new Date(validatedFrom).getTime();
      const toTime = new Date(validatedTo).getTime();
    
      const validPRs = allPRs
        .filter((node: any) => {
          // All nodes from Step 2 should already be PRs with full data
          // Just apply time range and repository filters
          if (!node.id || !node.repository) {
            return false;
          }
    
          // Apply time range filter (createdAt should already be in range from search, but double-check)
          const createdAt = new Date(node.createdAt).getTime();
          if (createdAt < fromTime || createdAt > toTime) {
            return false;
          }
    
          // Apply repository filter
          const repoName = node.repository.nameWithOwner.toLowerCase();
          if (!normalizedRepos.some(repo => repo === repoName)) {
            return false;
          }
    
          // Filter out auto-created PRs (e.g., backmerge PRs)
          if (this.isAutoCreatedPR(node.title)) {
            return false;
          }
    
          return true;
        })
        .map((node: any) => {
          try {
            return mapAuthoredPR(node);
          } catch (error: any) {
            console.error(`[Error] Failed to map PR node:`, error.message);
            return null;
          }
        })
        .filter((pr: any) => pr !== null) as AuthoredPR[];
    
      return { prs: validPRs };
    }
  • MCP server request handler registration for github.getAuthoredPRs. Dispatches tool calls to the GitHubTools.getAuthoredPRs method and formats the response as MCP content.
    case 'github.getAuthoredPRs': {
      const result = await tools.getAuthoredPRs(
        args.username as string,
        args.repos as string[],
        args.from as string | undefined,
        args.to as string | undefined
      );
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    }
  • Tool schema definition including name, description, inputSchema with properties (username, repos, from, to), required fields, and examples for github.getAuthoredPRs.
        {
          name: 'github.getAuthoredPRs',
          description: `Fetch all pull requests authored by a given user within a time range. Returns PRs with metadata including state (OPEN/MERGED/CLOSED), creation/merge dates, code statistics (files changed, additions, deletions), and repository information. Optionally filter by specific repositories. Automatically filters out auto-generated PRs (e.g., backmerge PRs). Use this tool to analyze a user's code contribution activity.
    
    Example use cases:
    - Assess developer productivity by counting PRs authored in a time period
    - Analyze code contribution trends over time
    - Get detailed metrics for performance reviews
    - Track PR merge rates and code change statistics
    
    Returns: Array of PR objects with id, repo, title, createdAt, mergedAt, state, filesChanged, additions, deletions`,
          inputSchema: {
            type: 'object',
            properties: {
              username: {
                type: 'string',
                description: 'GitHub username (case-insensitive, @ prefix optional). Examples: "octocat", "@octocat", "JohnDoe"',
                examples: ['octocat', '@octocat'],
              },
              repos: {
                type: 'array',
                items: { type: 'string' },
                description: 'Array of repositories in owner/repo format (required, at least one). Example: ["owner/repo1", "owner/repo2"]',
                examples: [['owner/repo'], ['owner/repo1', 'owner/repo2']],
              },
              from: {
                type: 'string',
                description: 'Optional: Start timestamp in ISO 8601 format. Must be before "to" parameter if provided. If omitted, uses last 3 months. Example: "2024-01-01T00:00:00Z"',
                examples: ['2024-01-01T00:00:00Z', '2024-06-01T00:00:00Z'],
              },
              to: {
                type: 'string',
                description: 'Optional: End timestamp in ISO 8601 format. Must be after "from" parameter if provided. If omitted, uses last 3 months. Example: "2024-12-31T23:59:59Z"',
                examples: ['2024-12-31T23:59:59Z', '2024-06-30T23:59:59Z'],
              },
            },
            required: ['username', 'repos'],
          },
        },
  • Shared validation helper used by getAuthoredPRs and other tools for common parameters (username, repos, time range).
    private validateCommonParameters(
      username: string,
      repos: string[],
      from?: string,
      to?: string
    ): { normalizedUsername: string; normalizedRepos: string[]; from: string; to: string } {
      const normalizedUsername = this.validateUsernameParameter(username);
      const normalizedRepos = this.validateReposParameter(repos);
      const timeRange = this.validateTimeRangeParameters(from, to);
    
      return {
        normalizedUsername,
        normalizedRepos,
        from: timeRange.from,
        to: timeRange.to
      };
    }
  • Helper function to filter out auto-created PRs (e.g., backmerge PRs) used in getAuthoredPRs filtering logic.
    private isAutoCreatedPR(prTitle: string | null | undefined): boolean {
      if (!prTitle) return false;
      const title = prTitle.trim();
      return title.toLowerCase().startsWith('backmerge:');
    }

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/radireddy/github-mcp'

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