Skip to content

Commit 855b07e

Browse files
authored
Merge branch 'main' into feature/github-packages-support
2 parents d710697 + 84ae009 commit 855b07e

File tree

9 files changed

+494
-39
lines changed

9 files changed

+494
-39
lines changed

README.md

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,12 @@ Alternatively, to manually configure VS Code, choose the appropriate JSON block
8787
8888
### Configuration
8989

90-
#### Default toolset configuration
91-
92-
The default configuration is:
93-
- context
94-
- repos
95-
- issues
96-
- pull_requests
97-
- users
90+
#### Toolset configuration
9891

9992
See [Remote Server Documentation](docs/remote-server.md) for full details on remote server configuration, toolsets, headers, and advanced usage. This file provides comprehensive instructions and examples for connecting, customizing, and installing the remote GitHub MCP Server in VS Code and other MCP hosts.
10093

94+
When no toolsets are specified, [default toolsets](#default-toolset) are used.
95+
10196
#### Enterprise Cloud with data residency (ghe.com)
10297

10398
GitHub Enterprise Cloud can also make use of the remote server.
@@ -329,7 +324,7 @@ The GitHub MCP Server supports enabling or disabling specific groups of function
329324

330325
_Toolsets are not limited to Tools. Relevant MCP Resources and Prompts are also included where applicable._
331326

332-
The Local GitHub MCP Server follows the same [default toolset configuration](#default-toolset-configuration) as the remote version.
327+
When no toolsets are specified, [default toolsets](#default-toolset) are used.
333328

334329
#### Specifying Toolsets
335330

@@ -359,7 +354,9 @@ docker run -i --rm \
359354
ghcr.io/github/github-mcp-server
360355
```
361356

362-
### The "all" Toolset
357+
### Special toolsets
358+
359+
#### "all" toolset
363360

364361
The special toolset `all` can be provided to enable all available toolsets regardless of any other configuration:
365362

@@ -373,9 +370,25 @@ Or using the environment variable:
373370
GITHUB_TOOLSETS="all" ./github-mcp-server
374371
```
375372

373+
#### "default" toolset
374+
The default toolset `default` is the configuration that gets passed to the server if no toolsets are specified.
375+
376+
The default configuration is:
377+
- context
378+
- repos
379+
- issues
380+
- pull_requests
381+
- users
382+
383+
To keep the default configuration and add additional toolsets:
384+
385+
```bash
386+
GITHUB_TOOLSETS="default,stargazers" ./github-mcp-server
387+
```
388+
376389
### Available Toolsets
377390

378-
The following sets of tools are available (all are on by default):
391+
The following sets of tools are available:
379392

380393
<!-- START AUTOMATED TOOLSETS -->
381394
| Toolset | Description |
@@ -401,6 +414,14 @@ The following sets of tools are available (all are on by default):
401414
| `users` | GitHub User related tools |
402415
<!-- END AUTOMATED TOOLSETS -->
403416

417+
### Additional Toolsets in Remote Github MCP Server
418+
419+
| Toolset | Description |
420+
| ----------------------- | ------------------------------------------------------------- |
421+
| `copilot` | Copilot related tools (e.g. Copilot Coding Agent) |
422+
| `copilot_spaces` | Copilot Spaces related tools |
423+
| `github_support_docs_search` | Search docs to answer GitHub product and support questions |
424+
404425
## Tools
405426

406427
<!-- START AUTOMATED TOOLS -->
@@ -1227,7 +1248,7 @@ Possible options:
12271248

12281249
<details>
12291250

1230-
<summary>Copilot coding agent</summary>
1251+
<summary>Copilot</summary>
12311252

12321253
- **create_pull_request_with_copilot** - Perform task with GitHub Copilot coding agent
12331254
- `owner`: Repository owner. You can guess the owner, but confirm it with the user before proceeding. (string, required)
@@ -1249,6 +1270,14 @@ Possible options:
12491270
- **list_copilot_spaces** - List Copilot Spaces
12501271
</details>
12511272

1273+
<details>
1274+
1275+
<summary>GitHub Support Docs Search</summary>
1276+
1277+
- **github_support_docs_search** - Retrieve documentation relevant to answer GitHub product and support questions. Support topics include: GitHub Actions Workflows, Authentication, GitHub Support Inquiries, Pull Request Practices, Repository Maintenance, GitHub Pages, GitHub Packages, GitHub Discussions, Copilot Spaces
1278+
- `query`: Input from the user about the question they need answered. This is the latest raw unedited user message. You should ALWAYS leave the user message as it is, you should never modify it. (string, required)
1279+
</details>
1280+
12521281
## Dynamic Tool Discovery
12531282

12541283
**Note**: This feature is currently in beta and may not be available in all environments. Please test it out and let us know if you encounter any issues.

cmd/github-mcp-server/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ var (
4545
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
4646
}
4747

48+
// No passed toolsets configuration means we enable the default toolset
4849
if len(enabledToolsets) == 0 {
49-
enabledToolsets = github.GetDefaultToolsetIDs()
50+
enabledToolsets = []string{github.ToolsetMetadataDefault.ID}
5051
}
5152

5253
stdioServerConfig := ghmcp.StdioServerConfig{

docs/installation-guides/install-gemini-cli.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ For security, avoid hardcoding your token. Create or update `~/.gemini/.env` (wh
1414

1515
```bash
1616
# ~/.gemini/.env
17-
GITHUB_PAT=your_token_here
17+
GITHUB_MCP_PAT=your_token_here
1818
```
1919

2020
</details>
@@ -30,26 +30,34 @@ After securely storing your PAT, you can add the GitHub MCP server configuration
3030

3131
> **Note:** For the most up-to-date configuration options, see the [main README.md](../../README.md).
3232
33-
### Method 1: Remote Server (Recommended)
33+
### Method 1: Gemini Extension (Recommended)
3434

35-
The simplest way is to use GitHub's hosted MCP server:
35+
The simplest way is to use GitHub's hosted MCP server via our gemini extension.
36+
37+
`gemini extensions install https://github.com/github/github-mcp-server`
38+
39+
> [!NOTE]
40+
> You will still need to have a personal access token with the appropriate scopes called `GITHUB_MCP_PAT` in your environment.
41+
42+
### Method 2: Remote Server
43+
44+
You can also connect to the hosted MCP server directly. After securely storing your PAT, configure Gemini CLI with:
3645

3746
```json
3847
// ~/.gemini/settings.json
3948
{
4049
"mcpServers": {
4150
"github": {
4251
"httpUrl": "https://api.githubcopilot.com/mcp/",
43-
"trust": true,
4452
"headers": {
45-
"Authorization": "Bearer $GITHUB_PAT"
53+
"Authorization": "Bearer $GITHUB_MCP_PAT"
4654
}
4755
}
4856
}
4957
}
5058
```
5159

52-
### Method 2: Local Docker
60+
### Method 3: Local Docker
5361

5462
With docker running, you can run the GitHub MCP server in a container:
5563

@@ -68,14 +76,14 @@ With docker running, you can run the GitHub MCP server in a container:
6876
"ghcr.io/github/github-mcp-server"
6977
],
7078
"env": {
71-
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_PAT"
79+
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_PAT"
7280
}
7381
}
7482
}
7583
}
7684
```
7785

78-
### Method 3: Binary
86+
### Method 4: Binary
7987

8088
You can download the latest binary release from the [GitHub releases page](https://github.com/github/github-mcp-server/releases) or build it from source by running `go build -o github-mcp-server ./cmd/github-mcp-server`.
8189

@@ -89,7 +97,7 @@ Then, replacing `/path/to/binary` with the actual path to your binary, configure
8997
"command": "/path/to/binary",
9098
"args": ["stdio"],
9199
"env": {
92-
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_PAT"
100+
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_PAT"
93101
}
94102
}
95103
}
@@ -122,6 +130,10 @@ To verify that the GitHub MCP server has been configured, start Gemini CLI in yo
122130
List my GitHub repositories
123131
```
124132
133+
## Additional Configuration
134+
135+
You can find more MCP configuration options for Gemini CLI here: [MCP Configuration Structure](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html#configuration-structure). For example, bypassing tool confirmations or excluding specific tools.
136+
125137
## Troubleshooting
126138
127139
### Local Server Issues

docs/remote-server.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ These toolsets are only available in the remote GitHub MCP Server and are not in
4747

4848
| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |
4949
| -------------------- | --------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50-
| Copilot coding agent | Perform task with GitHub Copilot coding agent | https://api.githubcopilot.com/mcp/x/copilot | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/copilot/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot%2Freadonly%22%7D) |
50+
| Copilot | Copilot related tools | https://api.githubcopilot.com/mcp/x/copilot | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/copilot/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot%2Freadonly%22%7D) |
51+
| Copilot Spaces | Copilot Spaces tools | https://api.githubcopilot.com/mcp/x/copilot_spaces | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot_spaces&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot_spaces%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/copilot_spaces/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-copilot_spaces&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcopilot_spaces%2Freadonly%22%7D) |
52+
| GitHub support docs search | Retrieve documentation to answer GitHub product and support questions. Topics include: GitHub Actions Workflows, Authentication, ... | https://api.githubcopilot.com/mcp/x/github_support_docs_search | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-support&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgithub_support_docs_search%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/github_support_docs_search/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-support&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgithub_support_docs_search%2Freadonly%22%7D) |
5153

5254
### Optional Headers
5355

gemini-extension.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "github",
3+
"version": "1.0.0",
4+
"mcpServers": {
5+
"github": {
6+
"description": "Connect AI assistants to GitHub - manage repos, issues, PRs, and workflows through natural language.",
7+
"httpUrl": "https://api.githubcopilot.com/mcp/",
8+
"headers": {
9+
"Authorization": "Bearer $GITHUB_MCP_PAT"
10+
}
11+
}
12+
}
13+
}

internal/ghmcp/server.go

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os/signal"
1313
"strings"
1414
"syscall"
15+
"time"
1516

1617
"github.com/github/github-mcp-server/pkg/errors"
1718
"github.com/github/github-mcp-server/pkg/github"
@@ -105,15 +106,10 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
105106
},
106107
}
107108

108-
enabledToolsets := cfg.EnabledToolsets
109-
if cfg.DynamicToolsets {
110-
// filter "all" from the enabled toolsets
111-
enabledToolsets = make([]string, 0, len(cfg.EnabledToolsets))
112-
for _, toolset := range cfg.EnabledToolsets {
113-
if toolset != "all" {
114-
enabledToolsets = append(enabledToolsets, toolset)
115-
}
116-
}
109+
enabledToolsets, invalidToolsets := cleanToolsets(cfg.EnabledToolsets, cfg.DynamicToolsets)
110+
111+
if len(invalidToolsets) > 0 {
112+
fmt.Fprintf(os.Stderr, "Invalid toolsets ignored: %s\n", strings.Join(invalidToolsets, ", "))
117113
}
118114

119115
// Generate instructions based on enabled toolsets
@@ -363,11 +359,30 @@ func newGHESHost(hostname string) (apiHost, error) {
363359
return apiHost{}, fmt.Errorf("failed to parse GHES GraphQL URL: %w", err)
364360
}
365361

366-
uploadURL, err := url.Parse(fmt.Sprintf("%s://%s/api/uploads/", u.Scheme, u.Hostname()))
362+
// Check if subdomain isolation is enabled
363+
// See https://docs.github.com/en/[email protected]/admin/configuring-settings/hardening-security-for-your-enterprise/enabling-subdomain-isolation#about-subdomain-isolation
364+
hasSubdomainIsolation := checkSubdomainIsolation(u.Scheme, u.Hostname())
365+
366+
var uploadURL *url.URL
367+
if hasSubdomainIsolation {
368+
// With subdomain isolation: https://uploads.hostname/
369+
uploadURL, err = url.Parse(fmt.Sprintf("%s://uploads.%s/", u.Scheme, u.Hostname()))
370+
} else {
371+
// Without subdomain isolation: https://hostname/api/uploads/
372+
uploadURL, err = url.Parse(fmt.Sprintf("%s://%s/api/uploads/", u.Scheme, u.Hostname()))
373+
}
367374
if err != nil {
368375
return apiHost{}, fmt.Errorf("failed to parse GHES Upload URL: %w", err)
369376
}
370-
rawURL, err := url.Parse(fmt.Sprintf("%s://%s/raw/", u.Scheme, u.Hostname()))
377+
378+
var rawURL *url.URL
379+
if hasSubdomainIsolation {
380+
// With subdomain isolation: https://raw.hostname/
381+
rawURL, err = url.Parse(fmt.Sprintf("%s://raw.%s/", u.Scheme, u.Hostname()))
382+
} else {
383+
// Without subdomain isolation: https://hostname/raw/
384+
rawURL, err = url.Parse(fmt.Sprintf("%s://%s/raw/", u.Scheme, u.Hostname()))
385+
}
371386
if err != nil {
372387
return apiHost{}, fmt.Errorf("failed to parse GHES Raw URL: %w", err)
373388
}
@@ -380,6 +395,29 @@ func newGHESHost(hostname string) (apiHost, error) {
380395
}, nil
381396
}
382397

398+
// checkSubdomainIsolation detects if GitHub Enterprise Server has subdomain isolation enabled
399+
// by attempting to ping the raw.<host>/_ping endpoint on the subdomain. The raw subdomain must always exist for subdomain isolation.
400+
func checkSubdomainIsolation(scheme, hostname string) bool {
401+
subdomainURL := fmt.Sprintf("%s://raw.%s/_ping", scheme, hostname)
402+
403+
client := &http.Client{
404+
Timeout: 5 * time.Second,
405+
// Don't follow redirects - we just want to check if the endpoint exists
406+
//nolint:revive // parameters are required by http.Client.CheckRedirect signature
407+
CheckRedirect: func(req *http.Request, via []*http.Request) error {
408+
return http.ErrUseLastResponse
409+
},
410+
}
411+
412+
resp, err := client.Get(subdomainURL)
413+
if err != nil {
414+
return false
415+
}
416+
defer resp.Body.Close()
417+
418+
return resp.StatusCode == http.StatusOK
419+
}
420+
383421
// Note that this does not handle ports yet, so development environments are out.
384422
func parseAPIHost(s string) (apiHost, error) {
385423
if s == "" {
@@ -427,3 +465,57 @@ func (t *bearerAuthTransport) RoundTrip(req *http.Request) (*http.Response, erro
427465
req.Header.Set("Authorization", "Bearer "+t.token)
428466
return t.transport.RoundTrip(req)
429467
}
468+
469+
// cleanToolsets cleans and handles special toolset keywords:
470+
// - Duplicates are removed from the result
471+
// - Removes whitespaces
472+
// - Validates toolset names and returns invalid ones separately
473+
// - "all": Returns ["all"] immediately, ignoring all other toolsets
474+
// - when dynamicToolsets is true, filters out "all" from the enabled toolsets
475+
// - "default": Replaces with the actual default toolset IDs from GetDefaultToolsetIDs()
476+
// Returns: (validToolsets, invalidToolsets)
477+
func cleanToolsets(enabledToolsets []string, dynamicToolsets bool) ([]string, []string) {
478+
seen := make(map[string]bool)
479+
result := make([]string, 0, len(enabledToolsets))
480+
invalid := make([]string, 0)
481+
validIDs := github.GetValidToolsetIDs()
482+
483+
// Add non-default toolsets, removing duplicates and trimming whitespace
484+
for _, toolset := range enabledToolsets {
485+
trimmed := strings.TrimSpace(toolset)
486+
if trimmed == "" {
487+
continue
488+
}
489+
if !seen[trimmed] {
490+
seen[trimmed] = true
491+
if trimmed != github.ToolsetMetadataDefault.ID && trimmed != github.ToolsetMetadataAll.ID {
492+
// Validate the toolset name
493+
if validIDs[trimmed] {
494+
result = append(result, trimmed)
495+
} else {
496+
invalid = append(invalid, trimmed)
497+
}
498+
}
499+
}
500+
}
501+
502+
hasDefault := seen[github.ToolsetMetadataDefault.ID]
503+
hasAll := seen[github.ToolsetMetadataAll.ID]
504+
505+
// Handle "all" keyword - return early if not in dynamic mode
506+
if hasAll && !dynamicToolsets {
507+
return []string{github.ToolsetMetadataAll.ID}, invalid
508+
}
509+
510+
// Expand "default" keyword to actual default toolsets
511+
if hasDefault {
512+
for _, defaultToolset := range github.GetDefaultToolsetIDs() {
513+
if !seen[defaultToolset] {
514+
result = append(result, defaultToolset)
515+
seen[defaultToolset] = true
516+
}
517+
}
518+
}
519+
520+
return result, invalid
521+
}

0 commit comments

Comments
 (0)