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:');
    }
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It does well by specifying the return format ('Array of PR objects with id, repo, title...'), mentioning automatic filtering of auto-generated PRs, and indicating time range defaults ('If omitted, uses last 3 months'). However, it doesn't mention potential limitations like rate limits, authentication requirements, or pagination behavior.

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

Conciseness4/5

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

The description is well-structured with a clear purpose statement, behavioral details, usage guidance, and output specification. While somewhat lengthy, every section adds value. The example use cases could potentially be more concise, but they provide helpful context for tool selection.

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?

For a tool with no annotations and no output schema, the description does a good job covering behavior, use cases, and return format. It specifies what data is returned and includes important behavioral details like auto-filtering and time defaults. The main gap is lack of information about authentication, rate limits, or error conditions.

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 fully documents all four parameters. The description adds minimal parameter semantics beyond the schema - it mentions 'Optionally filter by specific repositories' which is already clear in the schema. The description focuses more on use cases and outputs rather than parameter details.

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 specific action ('fetch all pull requests authored by a given user within a time range') and distinguishes it from sibling tools by specifying it's for analyzing user code contribution activity rather than reviews, comments, or repo stats. It explicitly mentions filtering out auto-generated PRs, which further differentiates its scope.

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 description provides explicit usage guidance with 'Use this tool to analyze a user's code contribution activity' and lists four concrete example use cases (assess productivity, analyze trends, performance reviews, track metrics). This clearly indicates when to use this tool versus alternatives like review or comment analysis 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/radireddy/github-mcp'

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