Skip to content
Closed
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
6 changes: 6 additions & 0 deletions src/github/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ RUN --mount=type=cache,target=/root/.npm npm install

FROM node:22.12-alpine AS release

# Install GitHub CLI
RUN apk add --no-cache curl git bash && \
curl -Ls https://github.com/cli/cli/releases/download/v2.44.1/gh_2.44.1_linux_amd64.tar.gz | tar -xz -C /tmp && \
mv /tmp/gh_*_linux_amd64/bin/gh /usr/local/bin/ && \
rm -rf /tmp/gh_*_linux_amd64

COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/package-lock.json /app/package-lock.json
Expand Down
68 changes: 66 additions & 2 deletions src/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,18 +303,47 @@ For detailed search syntax, see [GitHub's searching documentation](https://docs.

## Setup

### Personal Access Token
### Authentication

This server supports two authentication methods:

#### 1. GitHub CLI Authentication (Recommended)

The server will automatically use your GitHub CLI authentication if available:

1. Install the GitHub CLI:
- MacOS: `brew install gh`
- Other platforms: [GitHub CLI installation instructions](https://github.com/cli/cli#installation)

2. Authenticate with GitHub:
```bash
gh auth login
```

3. Follow the prompts to authenticate. Make sure to select the HTTPS protocol.

4. Verify your authentication:
```bash
gh auth status
```

#### 2. Personal Access Token

Alternatively, you can use a Personal Access Token:

[Create a GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with appropriate permissions:
- Go to [Personal access tokens](https://github.com/settings/tokens) (in GitHub Settings > Developer settings)
- 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
- Copy the generated token

Then set the environment variable `GITHUB_PERSONAL_ACCESS_TOKEN` with your token.

### Usage with Claude Desktop
To use this with Claude Desktop, add the following to your `claude_desktop_config.json`:

#### Docker
#### Docker with Personal Access Token
```json
{
"mcpServers": {
Expand All @@ -336,8 +365,28 @@ To use this with Claude Desktop, add the following to your `claude_desktop_confi
}
```

#### Docker with GitHub CLI Auth
```json
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"~/.config/gh:/root/.config/gh",
"mcp/github"
]
}
}
}
```

### NPX

#### NPX with Personal Access Token
```json
{
"mcpServers": {
Expand All @@ -355,6 +404,21 @@ To use this with Claude Desktop, add the following to your `claude_desktop_confi
}
```

#### NPX with GitHub CLI Auth
```json
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
]
}
}
}
```

## Build

Docker build:
Expand Down
51 changes: 51 additions & 0 deletions src/github/TEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# GitHub MCP Server Test

This directory contains a test script for the GitHub MCP server, which validates its core functionality.

## Test Summary

The test script (`test.js`) validates:

1. **Authentication**: Checks if GitHub CLI authentication is available
2. **List Tools**: Verifies the server can list all available tools
3. **Repository Search**: Tests searching for GitHub repositories
4. **File Content Retrieval**: Tests retrieving file contents from a repository

## Running Tests

Make sure you've built the server first:

```bash
npm install
npm run build
```

Then run the test:

```bash
node test.js
```

## Authentication

For complete testing, authenticate with GitHub CLI:

```bash
gh auth login
```

## Test Results

A successful test run will show:

- ✅ Tools listing test passed
- ✅ Repository search test passed
- ✅ Get file contents test passed (if authenticated)

## Troubleshooting

If tests fail:

1. Make sure the server is built correctly (`npm run build`)
2. Check GitHub CLI authentication status (`gh auth status`)
3. Verify network connectivity to GitHub API
61 changes: 59 additions & 2 deletions src/github/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,48 @@ export function buildUrl(baseUrl: string, params: Record<string, string | number

const USER_AGENT = `modelcontextprotocol/servers/github/v${VERSION} ${getUserAgent()}`;

// Cache the GitHub token to avoid frequent subprocess calls
let cachedGitHubToken: string | null = null;
let tokenExpiryTime: number = 0;
const TOKEN_CACHE_DURATION = 3600000; // 1 hour in milliseconds

async function getGitHubToken(): Promise<string | null> {
// Check if we have a valid cached token
const now = Date.now();
if (cachedGitHubToken && now < tokenExpiryTime) {
return cachedGitHubToken;
}

// Try to get token from environment variable first
if (process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
cachedGitHubToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
tokenExpiryTime = now + TOKEN_CACHE_DURATION;
return cachedGitHubToken;
}

// Try to get token from gh CLI
try {
const { execSync } = await import('child_process');
const output = execSync('gh auth token').toString().trim();

if (output) {
cachedGitHubToken = output;
tokenExpiryTime = now + TOKEN_CACHE_DURATION;
return cachedGitHubToken;
}
} catch (error) {
console.error("Failed to get GitHub token from gh CLI:", error);
}

console.warn(
"No GitHub authentication found. Please either:\n" +
"1. Set GITHUB_PERSONAL_ACCESS_TOKEN environment variable, or\n" +
"2. Authenticate with GitHub CLI using 'gh auth login'"
);

return null;
}

export async function githubRequest(
url: string,
options: RequestOptions = {}
Expand All @@ -39,8 +81,23 @@ export async function githubRequest(
...options.headers,
};

if (process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
headers["Authorization"] = `Bearer ${process.env.GITHUB_PERSONAL_ACCESS_TOKEN}`;
// Get token from gh CLI or environment variable
const token = await getGitHubToken();
if (token) {
headers["Authorization"] = `Bearer ${token}`;
} else {
// For operations requiring authentication, it's better to fail early
// Public GitHub APIs will still work without a token
if (
url.includes('/repos/') &&
(options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH' || options.method === 'DELETE')
) {
throw new Error(
"GitHub authentication required for this operation. Please either:\n" +
"1. Set the GITHUB_PERSONAL_ACCESS_TOKEN environment variable, or\n" +
"2. Authenticate with GitHub CLI using 'gh auth login'"
);
}
}

const response = await fetch(url, {
Expand Down
3 changes: 2 additions & 1 deletion src/github/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"scripts": {
"build": "tsc && shx chmod +x dist/*.js",
"prepare": "npm run build",
"watch": "tsc --watch"
"watch": "tsc --watch",
"test": "node ./test-github-server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "1.0.1",
Expand Down
Loading