Skip to content

Commit dce6266

Browse files
Merge branch 'main' into make-consolidated-projects-default
2 parents 4f60af5 + 15e66b3 commit dce6266

20 files changed

+1077
-479
lines changed

.github/workflows/conformance.yml

Lines changed: 0 additions & 69 deletions
This file was deleted.

.github/workflows/mcp-diff.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: MCP Server Diff
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
tags: ['v*']
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
mcp-diff:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Check out code
18+
uses: actions/checkout@v6
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Run MCP Server Diff
23+
uses: SamMorrowDrums/mcp-server-diff@v2
24+
with:
25+
setup_go: "true"
26+
install_command: go mod download
27+
start_command: go run ./cmd/github-mcp-server stdio
28+
env_vars: |
29+
GITHUB_PERSONAL_ACCESS_TOKEN=test-token
30+
configurations: |
31+
[
32+
{"name": "default", "args": ""},
33+
{"name": "read-only", "args": "--read-only"},
34+
{"name": "dynamic-toolsets", "args": "--dynamic-toolsets"},
35+
{"name": "read-only+dynamic", "args": "--read-only --dynamic-toolsets"},
36+
{"name": "toolsets-repos", "args": "--toolsets=repos"},
37+
{"name": "toolsets-issues", "args": "--toolsets=issues"},
38+
{"name": "toolsets-pull_requests", "args": "--toolsets=pull_requests"},
39+
{"name": "toolsets-repos,issues", "args": "--toolsets=repos,issues"},
40+
{"name": "toolsets-all", "args": "--toolsets=all"},
41+
{"name": "tools-get_me", "args": "--tools=get_me"},
42+
{"name": "tools-get_me,list_issues", "args": "--tools=get_me,list_issues"},
43+
{"name": "toolsets-repos+read-only", "args": "--toolsets=repos --read-only"},
44+
{"name": "toolsets-all+dynamic", "args": "--toolsets=all --dynamic-toolsets"},
45+
{"name": "toolsets-repos+dynamic", "args": "--toolsets=repos --dynamic-toolsets"},
46+
{"name": "toolsets-repos,issues+dynamic", "args": "--toolsets=repos,issues --dynamic-toolsets"},
47+
{
48+
"name": "dynamic-tool-calls",
49+
"args": "--dynamic-toolsets",
50+
"custom_messages": [
51+
{"id": 10, "name": "list_toolsets_before", "message": {"jsonrpc": "2.0", "id": 10, "method": "tools/call", "params": {"name": "list_available_toolsets", "arguments": {}}}},
52+
{"id": 11, "name": "get_toolset_tools", "message": {"jsonrpc": "2.0", "id": 11, "method": "tools/call", "params": {"name": "get_toolset_tools", "arguments": {"toolset": "repos"}}}},
53+
{"id": 12, "name": "enable_toolset", "message": {"jsonrpc": "2.0", "id": 12, "method": "tools/call", "params": {"name": "enable_toolset", "arguments": {"toolset": "repos"}}}},
54+
{"id": 13, "name": "list_toolsets_after", "message": {"jsonrpc": "2.0", "id": 13, "method": "tools/call", "params": {"name": "list_available_toolsets", "arguments": {}}}}
55+
]
56+
}
57+
]
58+
59+
- name: Add interpretation note
60+
if: always()
61+
run: |
62+
echo "" >> $GITHUB_STEP_SUMMARY
63+
echo "---" >> $GITHUB_STEP_SUMMARY
64+
echo "" >> $GITHUB_STEP_SUMMARY
65+
echo "ℹ️ **Note:** Differences may be intentional improvements." >> $GITHUB_STEP_SUMMARY
66+
echo "" >> $GITHUB_STEP_SUMMARY
67+
echo "Common expected differences:" >> $GITHUB_STEP_SUMMARY
68+
echo "- New tools/toolsets added" >> $GITHUB_STEP_SUMMARY
69+
echo "- Tool descriptions updated" >> $GITHUB_STEP_SUMMARY
70+
echo "- Capability changes (intentional improvements)" >> $GITHUB_STEP_SUMMARY

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,49 @@ See [Remote Server Documentation](docs/remote-server.md) for full details on rem
9898

9999
When no toolsets are specified, [default toolsets](#default-toolset) are used.
100100

101+
#### Insiders Mode
102+
103+
> **Try new features early!** The remote server offers an insiders version with early access to new features and experimental tools.
104+
105+
<table>
106+
<tr><th>Using URL Path</th><th>Using Header</th></tr>
107+
<tr valign=top>
108+
<td>
109+
110+
```json
111+
{
112+
"servers": {
113+
"github": {
114+
"type": "http",
115+
"url": "https://api.githubcopilot.com/mcp/insiders"
116+
}
117+
}
118+
}
119+
```
120+
121+
</td>
122+
<td>
123+
124+
```json
125+
{
126+
"servers": {
127+
"github": {
128+
"type": "http",
129+
"url": "https://api.githubcopilot.com/mcp/",
130+
"headers": {
131+
"X-MCP-Insiders": "true"
132+
}
133+
}
134+
}
135+
}
136+
```
137+
138+
</td>
139+
</tr>
140+
</table>
141+
142+
See [Remote Server Documentation](docs/remote-server.md#insiders-mode) for more details and examples.
143+
101144
#### GitHub Enterprise
102145

103146
##### GitHub Enterprise Cloud with data residency (ghe.com)
@@ -480,6 +523,31 @@ To keep the default configuration and add additional toolsets:
480523
GITHUB_TOOLSETS="default,stargazers" ./github-mcp-server
481524
```
482525

526+
### Insiders Mode
527+
528+
The local GitHub MCP Server offers an insiders version with early access to new features and experimental tools.
529+
530+
1. **Using Command Line Argument**:
531+
532+
```bash
533+
./github-mcp-server --insider-mode
534+
```
535+
536+
2. **Using Environment Variable**:
537+
538+
```bash
539+
GITHUB_INSIDER_MODE=true ./github-mcp-server
540+
```
541+
542+
When using Docker:
543+
544+
```bash
545+
docker run -i --rm \
546+
-e GITHUB_PERSONAL_ACCESS_TOKEN=<your-token> \
547+
-e GITHUB_INSIDER_MODE=true \
548+
ghcr.io/github/github-mcp-server
549+
```
550+
483551
### Available Toolsets
484552

485553
The following sets of tools are available:

docs/remote-server.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ The Remote GitHub MCP server has optional headers equivalent to the Local server
6767
- `X-MCP-Lockdown`: Enables lockdown mode, hiding public issue details created by users without push access.
6868
- Equivalent to `GITHUB_LOCKDOWN_MODE` env var for Local server.
6969
- If this header is empty, "false", "f", "no", "n", "0", or "off" (ignoring whitespace and case), it will be interpreted as false. All other values are interpreted as true.
70+
- `X-MCP-Insiders`: Enables insiders mode for early access to new features.
71+
- Equivalent to `GITHUB_INSIDER_MODE` env var or `--insider-mode` flag for Local server.
72+
- If this header is empty, "false", "f", "no", "n", "0", or "off" (ignoring whitespace and case), it will be interpreted as false. All other values are interpreted as true.
7073

7174
> **Looking for examples?** See the [Server Configuration Guide](./server-configuration.md) for common recipes like minimal setups, read-only mode, and combining tools with toolsets.
7275
@@ -84,18 +87,49 @@ Example:
8487
}
8588
```
8689

90+
### Insiders Mode
91+
92+
The remote GitHub MCP Server offers an insiders version with early access to new features and experimental tools. You can enable insiders mode in two ways:
93+
94+
1. **Via URL path** - Append `/insiders` to the URL:
95+
96+
```json
97+
{
98+
"type": "http",
99+
"url": "https://api.githubcopilot.com/mcp/insiders"
100+
}
101+
```
102+
103+
2. **Via header** - Set the `X-MCP-Insiders` header to `true`:
104+
105+
```json
106+
{
107+
"type": "http",
108+
"url": "https://api.githubcopilot.com/mcp/",
109+
"headers": {
110+
"X-MCP-Insiders": "true"
111+
}
112+
}
113+
```
114+
115+
Both methods can be combined with other path modifiers (like `/readonly`) and headers.
116+
87117
### URL Path Parameters
88118

89119
The Remote GitHub MCP server supports the following URL path patterns:
90120

91121
- `/` - Default toolset (see ["default" toolset](../README.md#default-toolset))
92122
- `/readonly` - Default toolset in read-only mode
123+
- `/insiders` - Default toolset with insiders mode enabled
124+
- `/insiders/readonly` - Default toolset with insiders mode in read-only mode
93125
- `/x/all` - All available toolsets
94126
- `/x/all/readonly` - All available toolsets in read-only mode
127+
- `/x/all/insiders` - All available toolsets with insiders mode enabled
95128
- `/x/{toolset}` - Single specific toolset
96129
- `/x/{toolset}/readonly` - Single specific toolset in read-only mode
130+
- `/x/{toolset}/insiders` - Single specific toolset with insiders mode enabled
97131

98-
Note: `{toolset}` can only be a single toolset, not a comma-separated list. To combine multiple toolsets, use the `X-MCP-Toolsets` header instead.
132+
Note: `{toolset}` can only be a single toolset, not a comma-separated list. To combine multiple toolsets, use the `X-MCP-Toolsets` header instead. Path modifiers like `/readonly` and `/insiders` can be combined with the `X-MCP-Insiders` or `X-MCP-Readonly` headers.
99133

100134
Example:
101135

internal/ghmcp/server.go

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -171,16 +171,31 @@ func NewMCPServer(cfg MCPServerConfig) (*mcp.Server, error) {
171171

172172
enabledToolsets := resolveEnabledToolsets(cfg)
173173

174-
// For instruction generation, we need actual toolset names (not nil).
175-
// nil means "use defaults" in inventory, so expand it for instructions.
176-
instructionToolsets := enabledToolsets
177-
if instructionToolsets == nil {
178-
instructionToolsets = github.GetDefaultToolsetIDs()
174+
// Create feature checker
175+
featureChecker := createFeatureChecker(cfg.EnabledFeatures)
176+
177+
// Build and register the tool/resource/prompt inventory
178+
inventoryBuilder := github.NewInventory(cfg.Translator).
179+
WithDeprecatedAliases(github.DeprecatedToolAliases).
180+
WithReadOnly(cfg.ReadOnly).
181+
WithToolsets(enabledToolsets).
182+
WithTools(cfg.EnabledTools).
183+
WithFeatureChecker(featureChecker).
184+
WithServerInstructions()
185+
186+
// Apply token scope filtering if scopes are known (for PAT filtering)
187+
if cfg.TokenScopes != nil {
188+
inventoryBuilder = inventoryBuilder.WithFilter(github.CreateToolScopeFilter(cfg.TokenScopes))
189+
}
190+
191+
inventory, err := inventoryBuilder.Build()
192+
if err != nil {
193+
return nil, fmt.Errorf("failed to build inventory: %w", err)
179194
}
180195

181196
// Create the MCP server
182197
serverOpts := &mcp.ServerOptions{
183-
Instructions: github.GenerateInstructions(instructionToolsets),
198+
Instructions: inventory.Instructions(),
184199
Logger: cfg.Logger,
185200
CompletionHandler: github.CompletionsHandler(func(_ context.Context) (*gogithub.Client, error) {
186201
return clients.rest, nil
@@ -203,9 +218,6 @@ func NewMCPServer(cfg MCPServerConfig) (*mcp.Server, error) {
203218
ghServer.AddReceivingMiddleware(addGitHubAPIErrorToContext)
204219
ghServer.AddReceivingMiddleware(addUserAgentsMiddleware(cfg, clients.rest, clients.gqlHTTP))
205220

206-
// Create feature checker
207-
featureChecker := createFeatureChecker(cfg.EnabledFeatures)
208-
209221
// Create dependencies for tool handlers
210222
deps := github.NewBaseDeps(
211223
clients.rest,
@@ -228,24 +240,6 @@ func NewMCPServer(cfg MCPServerConfig) (*mcp.Server, error) {
228240
}
229241
})
230242

231-
// Build and register the tool/resource/prompt inventory
232-
inventoryBuilder := github.NewInventory(cfg.Translator).
233-
WithDeprecatedAliases(github.DeprecatedToolAliases).
234-
WithReadOnly(cfg.ReadOnly).
235-
WithToolsets(enabledToolsets).
236-
WithTools(cfg.EnabledTools).
237-
WithFeatureChecker(featureChecker)
238-
239-
// Apply token scope filtering if scopes are known (for PAT filtering)
240-
if cfg.TokenScopes != nil {
241-
inventoryBuilder = inventoryBuilder.WithFilter(github.CreateToolScopeFilter(cfg.TokenScopes))
242-
}
243-
244-
inventory, err := inventoryBuilder.Build()
245-
if err != nil {
246-
return nil, fmt.Errorf("failed to build inventory: %w", err)
247-
}
248-
249243
if unrecognized := inventory.UnrecognizedToolsets(); len(unrecognized) > 0 {
250244
fmt.Fprintf(os.Stderr, "Warning: unrecognized toolsets ignored: %s\n", strings.Join(unrecognized, ", "))
251245
}

pkg/github/__toolsnaps__/assign_copilot_to_issue.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
"type": "string"
2424
},
2525
"custom_instructions": {
26-
"type": "string",
27-
"description": "Optional custom instructions to guide the agent beyond the issue body. Use this to provide additional context, constraints, or guidance that is not captured in the issue description"
26+
"description": "Optional custom instructions to guide the agent beyond the issue body. Use this to provide additional context, constraints, or guidance that is not captured in the issue description",
27+
"type": "string"
2828
},
2929
"issue_number": {
3030
"description": "Issue number",

pkg/github/__toolsnaps__/projects_get.snap

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@
3131
"type": "string"
3232
},
3333
"owner": {
34-
"description": "If owner_type == user it is the handle for the GitHub user account. If owner_type == org it is the name of the organization. The name is not case sensitive.",
34+
"description": "The owner (user or organization login). The name is not case sensitive.",
3535
"type": "string"
3636
},
3737
"owner_type": {
38-
"description": "Owner type",
38+
"description": "Owner type (user or org). If not provided, will be automatically detected.",
3939
"enum": [
4040
"user",
4141
"org"
@@ -49,7 +49,6 @@
4949
},
5050
"required": [
5151
"method",
52-
"owner_type",
5352
"owner",
5453
"project_number"
5554
],

0 commit comments

Comments
 (0)