Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GitHub Actions support #780

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,367 changes: 2,288 additions & 79 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/github/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# GitHub Personal Access Token with `workflow` scope
# Create one at https://github.com/settings/tokens
GITHUB_PERSONAL_ACCESS_TOKEN=your_token_here

# E2E test repository info - use a repository with GitHub Actions workflows
E2E_TEST_OWNER=your_username_or_org
E2E_TEST_REPO=your_repository_name
140 changes: 140 additions & 0 deletions src/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ MCP Server for the GitHub API, enabling file operations, repository management,
- **Git History Preservation**: Operations maintain proper Git history without force pushing
- **Batch Operations**: Support for both single-file and multi-file operations
- **Advanced Search**: Support for searching code, issues/PRs, and users
- **GitHub Actions Integration**: Complete access to workflows, runs, jobs, and CI/CD status


## Tools
Expand Down Expand Up @@ -277,6 +278,144 @@ MCP Server for the GitHub API, enabling file operations, repository management,
- `pull_number` (number): Pull request number
- Returns: Array of pull request reviews with details like the review state (APPROVED, CHANGES_REQUESTED, etc.), reviewer, and review body

## GitHub Actions Integration

The GitHub Actions integration enables access to workflow runs, jobs, and CI/CD status. This is especially valuable for troubleshooting CI failures and understanding build status.

### GitHub Actions Tools

27. `list_workflows`
- List GitHub Actions workflows in a repository
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `page` (optional number): Page number for pagination
- `per_page` (optional number): Results per page (max 100)
- Returns: List of workflows with details including ID, name, state, file path, etc.

28. `get_workflow`
- Get a specific GitHub Actions workflow
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `workflow_id` (string|number): Workflow ID or filename
- Returns: Detailed workflow information

29. `list_workflow_runs`
- List GitHub Actions workflow runs
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `workflow_id` (optional string|number): Filter by specific workflow ID or filename
- `actor` (optional string): Filter by user who triggered the workflow
- `branch` (optional string): Filter by branch name
- `event` (optional string): Filter by event type (push, pull_request, etc.)
- `status` (optional string): Filter by status
- `per_page` (optional number): Results per page (max 100)
- `page` (optional number): Page number
- Returns: List of workflow runs with status, conclusion, and related details

30. `get_workflow_run`
- Get a specific GitHub Actions workflow run
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `run_id` (number): Run ID
- Returns: Detailed workflow run information

31. `list_workflow_jobs`
- List jobs for a workflow run
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `run_id` (number): Run ID
- `filter` (optional string): Filter jobs by completion status ('latest', 'all')
- `per_page` (optional number): Results per page (max 100)
- `page` (optional number): Page number
- Returns: List of jobs with steps, status, and conclusion details

32. `get_workflow_job`
- Get a specific job from a workflow run
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `job_id` (number): Job ID
- Returns: Detailed job information including steps and conclusion

33. `list_workflow_run_artifacts`
- List artifacts for a workflow run
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `run_id` (number): Run ID
- `per_page` (optional number): Results per page (max 100)
- `page` (optional number): Page number
- Returns: List of available artifacts with download URLs and expiration details

34. `download_workflow_run_logs`
- Get download URL for workflow run logs
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `run_id` (number): Run ID
- Returns: A download URL for the workflow run logs

35. `get_job_logs`
- Get download URL for job logs
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `job_id` (number): Job ID
- Returns: A download URL for the job logs

36. `get_recent_failed_runs`
- Get details of recent failed workflow runs with specific failed jobs and steps
- Inputs:
- `owner` (string): Repository owner (username or organization)
- `repo` (string): Repository name
- `limit` (optional number): Maximum number of failed runs to return
- Returns: Structured data about recent workflow failures with detailed information about what specifically failed

### Accessing GitHub Actions Logs

To access workflow and job logs, your GitHub Personal Access Token must have:

- The `workflow` scope (as already mentioned)
- The `repo` scope (for private repositories) or `repo:status` (for public repositories)
- The `actions:read` permission (for specific log access)

Without these permissions, log-related operations like `get_job_logs` and `download_workflow_run_logs` will return 401 Unauthorized errors.

### Testing the GitHub Actions Integration

To ensure the GitHub Actions integration works properly with your repositories, follow these steps:

1. **Set up a Personal Access Token:**
- Create a [GitHub Personal Access Token](https://github.com/settings/tokens) with the `workflow` scope
- Set the token as the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable

2. **Configure your test repository:**
- Copy `.env.example` to `.env` and fill in your values
- Use a repository with GitHub Actions workflows
- For testing failures, you can use the sample workflow in `examples/sample-workflow.yml`

3. **Run the E2E tests:**
```bash
npm run test:e2e
```

4. **Testing with Claude Desktop:**
- Ensure your Claude Desktop config includes the `workflow` scope in your GitHub token
- Try asking Claude to:
- "Show me recent workflow runs in my repository"
- "What workflows are failing in my repository?"
- "Help me debug the latest CI failure"

5. **Troubleshooting Tips:**
- Verify your token has the correct permissions (needs `workflow` scope)
- Check that your repository has GitHub Actions enabled
- For private repositories, ensure your token has access to the repository

## Search Query Syntax

### Code Search
Expand Down Expand Up @@ -309,6 +448,7 @@ For detailed search syntax, see [GitHub's searching documentation](https://docs.
- Select which repositories you'd like this token to have access to (Public, All, or Select)
- Create a token with the `repo` scope ("Full control of private repositories")
- Alternatively, if working only with public repositories, select only the `public_repo` scope
- If using GitHub Actions functionality, ensure you also select the `workflow` scope
- Copy the generated token

### Usage with Claude Desktop
Expand Down
90 changes: 90 additions & 0 deletions src/github/__tests__/actions.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, it, expect, beforeAll, afterEach } from 'vitest';
import type { TestContext } from 'vitest';
import * as actions from '../operations/actions.js';
import dotenv from 'dotenv';
import * as fs from 'fs';
import * as path from 'path';
import { GitHubAuthenticationError } from '../common/errors.js';

// Load environment variables from .env file
const envPath = path.resolve(process.cwd(), '.env');
if (fs.existsSync(envPath)) {
console.log(`Loading environment from ${envPath}`);
dotenv.config({ path: envPath });
} else {
console.log('No .env file found, using existing environment variables');
dotenv.config();
}

// Check for token and setup test flags
const hasToken = !!process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
const tokenValue = process.env.GITHUB_PERSONAL_ACCESS_TOKEN || '';
const validTokenFormat = tokenValue.startsWith('ghp_') || tokenValue.startsWith('github_pat_');
const testOwner = process.env.E2E_TEST_OWNER || 'facebook';
const testRepo = process.env.E2E_TEST_REPO || 'react';

// Setup conditional testing
const runTests = hasToken;
const itWithToken = runTests ? it : it.skip;

describe('GitHub Actions API E2E Tests', () => {
beforeAll(() => {
if (!hasToken) {
console.warn('⚠️ Skipping GitHub Actions E2E tests - GITHUB_PERSONAL_ACCESS_TOKEN not found');
console.warn('To run these tests, please create a .env file with your GitHub token');
return;
}

if (!validTokenFormat) {
console.warn('⚠️ Warning: Your GitHub token does not appear to be in the correct format');
console.warn('Valid tokens typically start with "ghp_" or "github_pat_"');
console.warn('You may encounter "Bad credentials" errors if the token is invalid');
}

console.log(`🔍 Running E2E tests against ${testOwner}/${testRepo}`);
});

afterEach((context: TestContext) => {
// @ts-ignore - The error property exists at runtime even if not in the type definitions
if (context.error instanceof GitHubAuthenticationError) {
console.error('\n❌ Authentication Error: Bad Credentials');
console.error('This means your GitHub token is invalid, expired, or lacks permissions');
console.error('To fix this:');
console.error('1. Go to https://github.com/settings/tokens');
console.error('2. Generate a new Personal Access Token with the "workflow" scope');
console.error('3. Update your .env file with the new token');
console.error('4. Run the tests again\n');
}
});

itWithToken('should list workflows from a real repository', async () => {
const result = await actions.listWorkflows(testOwner, testRepo);
expect(result).toBeDefined();
expect(result.total_count).toBeGreaterThanOrEqual(0);
expect(Array.isArray(result.workflows)).toBe(true);
console.log(`Found ${result.total_count} workflows`);
});

itWithToken('should fetch workflow runs from a real repository', async () => {
const result = await actions.listWorkflowRuns(testOwner, testRepo);
expect(result).toBeDefined();
expect(result.total_count).toBeGreaterThanOrEqual(0);
expect(Array.isArray(result.workflow_runs)).toBe(true);
console.log(`Found ${result.total_count} workflow runs`);
});

itWithToken('should fetch and potentially find failed runs', async () => {
const result = await actions.getRecentFailedRuns(testOwner, testRepo, 3);
expect(Array.isArray(result)).toBe(true);
console.log(`Found ${result.length} failed workflow runs`);

// We don't want to fail the test if there are no failures (that's good!)
if (result.length > 0) {
const failedRun = result[0];
expect(failedRun.run).toBeDefined();
expect(failedRun.run.id).toBeDefined();
expect(failedRun.run.conclusion).toBe('failure');
expect(Array.isArray(failedRun.failed_jobs)).toBe(true);
}
});
});
Loading