# GitHub MCP Server
A production-ready MCP (Model Context Protocol) server that exposes employee-centric GitHub activity APIs for AI agents and agent orchestration systems. This server abstracts GitHub REST & GraphQL APIs and provides semantic endpoints for analyzing developer contributions, code reviews, and impact over time ranges.
## 🎯 For AI Agents & Agent Orchestration
**Quick Start for Agents**: See [AGENT_GUIDE.md](./AGENT_GUIDE.md) for comprehensive agent-focused documentation, use cases, and examples.
**Recommended Tool for Quick Overview**: Use `github.getUserRepoStats` to get comprehensive metrics in a single call (PRs, comments, reviews, code changes).
**Tool Discovery**: All tools include detailed descriptions, examples, and use cases in their schemas for easy agent discovery.
## Features
- **Pull Request Analysis**: Fetch PRs authored by employees with detailed metadata
- **Review Tracking**: Get PR reviews with states (APPROVED, CHANGES_REQUESTED, COMMENTED)
- **Comment Analysis**: Extract inline and general review comments with file/line context
- **User Comments**: Fetch all comments (review and issue) by a user for a repository within a time range
- **Repository Statistics**: Get comprehensive stats (PRs, comments, reviews, code changes) for a user in a repository
- **Impact Assessment**: Analyze whether review comments led to code changes
- **Code Statistics**: Get detailed diff metadata and code stats for PRs
## Architecture
```
/src
/mcp
server.ts # MCP server registration
tools.ts # Tool definitions and handlers
/github
client.ts # GraphQL client with auth
queries.ts # GraphQL query definitions
mapper.ts # Normalize GitHub → MCP DTOs
/utils
pagination.ts # Cursor-based pagination
time.ts # ISO 8601 timestamp handling
rateLimit.ts # Rate limit management
```
## Prerequisites
- Node.js 20+
- GitHub Personal Access Token with appropriate permissions:
- `repo` (REQUIRED for private repos)
- `read:org` (REQUIRED for organization repositories)
- `read:user` (for user data)
### For Private Organization Repositories
If you're querying private organization repositories (e.g., `radireddy/AiApps`), ensure:
1. **Token has both scopes**: `repo` AND `read:org`
2. **Account membership**: Your account must be a member of the organization
3. **Organization settings**: Organization must allow third-party access (if using OAuth)
4. **Repository access**: You must have at least read access to the repository
To verify your token has access, run:
```bash
npm run check-token radireddy/AiApps
```
## Installation
```bash
npm install
npm run build
```
## Configuration
### Option 1: Using .env file (Recommended)
1. Copy the example file:
```bash
cp .env.example .env
```
2. Edit `.env` and add your GitHub token:
```bash
GITHUB_TOKEN=ghp_your_token_here
```
The `.env` file is automatically loaded and is excluded from git (already in `.gitignore`).
### Option 2: Environment Variable
```bash
export GITHUB_TOKEN=ghp_your_token_here
```
**Note:** The `.env` file approach is recommended as it keeps your token local and secure.
## Usage
### MCP Configuration
Add to your MCP client configuration (`mcp.json`):
```json
{
"mcpServers": {
"github": {
"command": "node",
"args": ["dist/mcp/server.js"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
```
### Running the Server
```bash
npm start
```
The server communicates via stdio, so it should be launched by your MCP client.
## MCP Tools
### 1. `github.getAuthoredPRs`
Fetch all PRs authored by a user in a time range.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo1", "owner/repo2"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Output:**
```json
{
"prs": [
{
"id": "PR_kwDO...",
"repo": "owner/repo",
"title": "Fix bug in authentication",
"createdAt": "2024-06-15T10:30:00Z",
"mergedAt": "2024-06-16T14:20:00Z",
"state": "MERGED",
"filesChanged": 5,
"additions": 120,
"deletions": 45
}
]
}
```
### 2. `github.getPRReviews`
Fetch PR reviews submitted by the employee. Filters by repository.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Output:**
```json
{
"reviews": [
{
"id": "PRR_kwDO...",
"state": "APPROVED",
"prId": "PR_kwDO...",
"prNumber": 123,
"prTitle": "Add feature X",
"prRepo": "owner/repo",
"submittedAt": "2024-06-20T09:15:00Z"
}
]
}
```
### 3. `github.getReviewComments`
Fetch inline and general review comments. Returns structured JSON with PR-level grouping, totals, and date range. Filters by repository.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Note:** The `repos` parameter is required (at least one repository). Only comments on PRs in the specified repositories will be included. `from` and `to` are optional (defaults to last 3 months if omitted).
**Output:**
```json
{
"userId": "octocat",
"dateRange": {
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
},
"totalPRsReviewed": 5,
"totalComments": 12,
"prs": [
{
"prId": "PR_kwDO...",
"prNumber": 123,
"prTitle": "Add feature X",
"prRepo": "owner/repo",
"prUrl": "https://github.com/owner/repo/pull/123",
"prCreatedAt": "2024-06-15T10:30:00Z",
"comments": [
"Consider using a constant here",
"This looks good!"
],
"totalComments": 2
},
{
"prId": "PR_kwDO...",
"prNumber": 124,
"prTitle": "Fix bug Y",
"prRepo": "owner/repo",
"prUrl": "https://github.com/owner/repo/pull/124",
"prCreatedAt": "2024-06-20T14:20:00Z",
"comments": [
"Maybe we should add error handling here"
],
"totalComments": 1
}
]
}
```
**Note:** All comment bodies are properly JSON-escaped to handle newlines (`\n`), quotes, and other special characters. The response is 100% valid JSON format.
### 4. `github.getCommentImpact`
Analyze whether review comments resulted in code changes. Filters by repository.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Output:**
```json
{
"impacts": [
{
"commentId": "PRRC_kwDO...",
"prId": "PR_kwDO...",
"hadImpact": true,
"confidence": 0.7,
"evidence": [
"Commit abc1234 modified files after comment (3 files)"
]
}
],
"stats": {
"totalComments": 25,
"totalPRsReviewed": 10,
"totalImpacts": 8
}
}
```
**Note:**
- Only comments with actual impact (commits found after comment) are included in the `impacts` array
- The `stats` object is only included if data is available (i.e., if comments were analyzed)
- Statistics are calculated from existing data without additional API calls
### 5. `github.getUserComments`
Fetch all comments (review comments and issue comments) added by a user for a given repository within a time duration. This tool combines PR review comments and PR issue comments, filters by `comment.createdAt` and author, normalizes the results, and deduplicates them.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Output:**
```json
{
"comments": [
{
"id": "PRRC_kwDO...",
"body": "Consider using a constant here",
"createdAt": "2024-06-20T09:15:00Z",
"author": "octocat",
"prId": "PR_kwDO...",
"prNumber": 123,
"prTitle": "Add feature X",
"prRepo": "owner/repo",
"commentType": "review",
"filePath": "src/utils/helper.ts",
"lineNumber": 42,
"reviewId": "PRR_kwDO..."
},
{
"id": "IC_kwDO...",
"body": "Great work! This looks good to me.",
"createdAt": "2024-06-21T14:30:00Z",
"author": "octocat",
"prId": "PR_kwDO...",
"prNumber": 123,
"prTitle": "Add feature X",
"prRepo": "owner/repo",
"commentType": "issue",
"filePath": null,
"lineNumber": null,
"reviewId": null
}
]
}
```
**Note:** This tool uses GitHub GraphQL APIs directly and filters by `comment.createdAt` client-side for accurate time-based filtering. It combines both review comments (inline comments from PR reviews) and issue comments (general comments on PRs), normalizes them into a unified format, and deduplicates by comment ID.
### 6. `github.getUserRepoStats`
Get comprehensive repository statistics for a user within a time frame. Aggregates all activity metrics including PRs, comments, reviews, and code changes.
**Input:**
```json
{
"username": "octocat",
"repos": ["owner/repo"],
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
}
```
**Note:** `from` and `to` are optional. If omitted, defaults to last 3 months. `repos` is required (at least one repository).
**Output:**
```json
{
"stats": {
"username": "octocat",
"repo": "owner/repo",
"timeRange": {
"from": "2024-01-01T00:00:00Z",
"to": "2024-12-31T23:59:59Z"
},
"prs": {
"count": 15,
"merged": 12,
"open": 2,
"closed": 1
},
"comments": {
"total": 45,
"review": 30,
"issue": 15
},
"reviews": {
"total": 20,
"totalPRsReviewed": 15,
"approved": 12,
"changesRequested": 5,
"commented": 3
},
"codeChanges": {
"filesChanged": 120,
"additions": 3500,
"deletions": 800,
"netChange": 2700
}
}
}
```
**Note:** This tool combines data from multiple sources (`getAuthoredPRs`, `getUserComments`, `getPRReviews`) to provide a complete overview of user activity in a repository. All statistics are filtered by the specified time range and repository.
**Important:** When comparing with `github.getReviewComments`:
- `getUserRepoStats.comments.total` includes both review comments AND issue comments, while `getReviewComments.totalComments` includes only review comments
- `getUserRepoStats.comments.review` should match `getReviewComments.totalComments` when both are filtered by the same repository
- `getUserRepoStats.reviews.totalPRsReviewed` counts unique PRs reviewed, while `getUserRepoStats.reviews.total` counts total review submissions (a user can review the same PR multiple times)
## Agent Orchestration
This MCP server is designed for use by AI agents and agent orchestration systems. All tools include:
- **Detailed descriptions** with use cases and examples
- **Parameter examples** in tool schemas
- **Consistent response formats** for easy parsing
- **Automatic filtering** of auto-generated content
- **Error handling** with clear error messages
### Quick Reference for Agents
| Tool | Best For | Returns |
|------|----------|---------|
| `github.getUserRepoStats` | **Quick overview** - Single call for all metrics | Complete stats (PRs, comments, reviews, code changes) |
| `github.getAuthoredPRs` | PR analysis | Array of PRs with metadata |
| `github.getPRReviews` | Review participation | Array of reviews with states |
| `github.getReviewComments` | Comment analysis | Grouped comments by PR |
| `github.getCommentImpact` | Review effectiveness | Impact assessments with confidence scores |
| `github.getUserComments` | All comments | Combined review + issue comments |
**See [AGENT_GUIDE.md](./AGENT_GUIDE.md) for comprehensive agent documentation, workflows, and best practices.**
**See [agent-examples.json](./agent-examples.json) for real-world agent usage examples and workflows.**
## Example Agent Call
Here's an example of how an AI agent might use this MCP server:
```typescript
// Agent workflow for collecting data for employee performance analysis
// 1. Get all PRs authored by employee in Q1 2024
const prs = await mcp.callTool('github.getAuthoredPRs', {
username: 'johndoe',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-03-31T23:59:59Z'
});
// 2. Get PR reviews to assess code review participation
const reviews = await mcp.callTool('github.getPRReviews', {
username: 'johndoe',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-03-31T23:59:59Z'
});
// 3. Analyze review comment impact
const impacts = await mcp.callTool('github.getCommentImpact', {
username: 'johndoe',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-03-31T23:59:59Z'
});
// 4. Get all comments by employee for a specific repository
const comments = await mcp.callTool('github.getUserComments', {
username: 'johndoe',
repos: ['company/important-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-03-31T23:59:59Z'
});
// 6. Get comprehensive repository statistics
const repoStats = await mcp.callTool('github.getUserRepoStats', {
username: 'johndoe',
repos: ['company/important-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-03-31T23:59:59Z'
});
// Returns aggregated stats: PRs, comments, reviews, code changes
```
## Design Decisions
### GraphQL over REST
- More efficient for nested data (reviews, comments, files)
- Single request for complex queries
- Better type safety
### Cursor-based Pagination
- Handles large datasets efficiently
- No duplicate results
- Predictable performance
### Normalized DTOs
- Consistent data structure for agents
- Hides GitHub API complexity
- Easy to extend
### Rate Limit Handling
- Automatic detection and waiting
- Request ID logging for debugging
- Graceful error messages
### Case-Insensitive Usernames
- User-friendly (handles @ prefix, case variations)
- Normalizes to lowercase for consistency
## Error Handling
The server handles:
- **Authentication errors**: Clear messages about GITHUB_TOKEN
- **Rate limits**: Automatic waiting and informative errors
- **Invalid timestamps**: Validation with helpful error messages
- **Missing data**: Graceful defaults (null, empty arrays)
## Extensibility
The architecture supports easy extension:
1. **New Tools**: Add to `tools.ts` and register in `server.ts`
2. **New Queries**: Add GraphQL queries to `queries.ts`
3. **New Mappers**: Extend `mapper.ts` with new DTOs
4. **Caching**: Add caching layer in `client.ts` (extension point)
## Development
```bash
# Build
npm run build
# Watch mode
npm run dev
# Run
npm start
```
## Agent Integration Examples
### Example 1: Performance Review Data Collection
```typescript
// Get comprehensive stats in one call (from/to optional, defaults to last 3 months)
const stats = await mcp.callTool('github.getUserRepoStats', {
username: 'johndoe',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
// Returns: Complete metrics that can be used for performance reviews
```
### Example 2: Code Review Quality Assessment
```typescript
// Step 1: Get review activity
const reviews = await mcp.callTool('github.getPRReviews', {
username: 'reviewer-name',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
// Step 2: Measure review impact
const impact = await mcp.callTool('github.getCommentImpact', {
username: 'reviewer-name',
repos: ['company/main-repo'],
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
// Calculate effectiveness: impact.stats.totalImpacts / impact.stats.totalComments
```
### Example 3: Multi-Repository Analysis
```typescript
// Option 1: Get stats for multiple repos separately
const repos = ['company/repo1', 'company/repo2'];
const results = await Promise.all(
repos.map(repo =>
mcp.callTool('github.getUserRepoStats', {
username: 'developer-name',
repos: [repo],
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
})
)
);
// Option 2: Get stats for multiple repos in one call (aggregated)
const aggregatedStats = await mcp.callTool('github.getUserRepoStats', {
username: 'developer-name',
repos: ['company/repo1', 'company/repo2'],
from: '2024-01-01T00:00:00Z',
to: '2024-12-31T23:59:59Z'
});
```
**For more examples and workflows, see [AGENT_GUIDE.md](./AGENT_GUIDE.md).**
## License
MIT