Skip to content
Merged
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
3 changes: 1 addition & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ All workflows run on push/PR unless noted. Located in `.github/workflows/`:
- **GITHUB_HOST** - For GitHub Enterprise Server (prefix with `https://`)
- **GITHUB_TOOLSETS** - Comma-separated toolset list (overrides --toolsets flag)
- **GITHUB_READ_ONLY** - Set to "1" for read-only mode
- **GITHUB_DYNAMIC_TOOLSETS** - Set to "1" for dynamic toolset discovery
- **UPDATE_TOOLSNAPS** - Set to "true" when running tests to update snapshots
- **GITHUB_MCP_SERVER_E2E_TOKEN** - Token for e2e tests
- **GITHUB_MCP_SERVER_E2E_DEBUG** - Set to "true" for in-process e2e debugging
Expand Down Expand Up @@ -273,7 +272,7 @@ server.json - MCP server registry metadata
`cmd/github-mcp-server/main.go` - Uses cobra for CLI, viper for config, supports:
- `stdio` command (default) - MCP stdio transport
- `generate-docs` command - Documentation generation
- Flags: --toolsets, --read-only, --dynamic-toolsets, --gh-host, --log-file
- Flags: --toolsets, --read-only, --gh-host, --log-file

## Important Reminders

Expand Down
17 changes: 1 addition & 16 deletions .github/workflows/mcp-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:
[
{"name": "default", "args": ""},
{"name": "read-only", "args": "--read-only"},
{"name": "dynamic-toolsets", "args": "--dynamic-toolsets"},
{"name": "read-only+dynamic", "args": "--read-only --dynamic-toolsets"},
{"name": "toolsets-repos", "args": "--toolsets=repos"},
{"name": "toolsets-issues", "args": "--toolsets=issues"},
{"name": "toolsets-context", "args": "--toolsets=context"},
Expand All @@ -45,20 +43,7 @@ jobs:
{"name": "toolsets-all", "args": "--toolsets=all"},
{"name": "tools-get_me", "args": "--tools=get_me"},
{"name": "tools-get_me,list_issues", "args": "--tools=get_me,list_issues"},
{"name": "toolsets-repos+read-only", "args": "--toolsets=repos --read-only"},
{"name": "toolsets-all+dynamic", "args": "--toolsets=all --dynamic-toolsets"},
{"name": "toolsets-repos+dynamic", "args": "--toolsets=repos --dynamic-toolsets"},
{"name": "toolsets-repos,issues+dynamic", "args": "--toolsets=repos,issues --dynamic-toolsets"},
{
"name": "dynamic-tool-calls",
"args": "--dynamic-toolsets",
"custom_messages": [
{"id": 10, "name": "list_toolsets_before", "message": {"jsonrpc": "2.0", "id": 10, "method": "tools/call", "params": {"name": "list_available_toolsets", "arguments": {}}}},
{"id": 11, "name": "get_toolset_tools", "message": {"jsonrpc": "2.0", "id": 11, "method": "tools/call", "params": {"name": "get_toolset_tools", "arguments": {"toolset": "repos"}}}},
{"id": 12, "name": "enable_toolset", "message": {"jsonrpc": "2.0", "id": 12, "method": "tools/call", "params": {"name": "enable_toolset", "arguments": {"toolset": "repos"}}}},
{"id": 13, "name": "list_toolsets_after", "message": {"jsonrpc": "2.0", "id": 13, "method": "tools/call", "params": {"name": "list_available_toolsets", "arguments": {}}}}
]
}
{"name": "toolsets-repos+read-only", "args": "--toolsets=repos --read-only"}
]

- name: Add interpretation note
Expand Down
35 changes: 2 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ The environment variable `GITHUB_TOOLSETS` takes precedence over the command lin

#### Specifying Individual Tools

You can also configure specific tools using the `--tools` flag. Tools can be used independently or combined with toolsets and dynamic toolsets discovery for fine-grained control.
You can also configure specific tools using the `--tools` flag. Tools can be used independently or combined with toolsets for fine-grained control.

1. **Using Command Line Argument**:

Expand All @@ -446,17 +446,9 @@ You can also configure specific tools using the `--tools` flag. Tools can be use

This registers all tools from `repos` and `issues` toolsets, plus `get_gist`.

4. **Combining with Dynamic Toolsets** (additive):

```bash
github-mcp-server --tools get_file_contents --dynamic-toolsets
```

This registers `get_file_contents` plus the dynamic toolset tools (`enable_toolset`, `list_available_toolsets`, `get_toolset_tools`).

**Important Notes:**

- Tools, toolsets, and dynamic toolsets can all be used together
- Tools and toolsets can be used together
- Read-only mode takes priority: write tools are skipped if `--read-only` is set, even if explicitly requested via `--tools`
- Tool names must match exactly (e.g., `get_file_contents`, not `getFileContents`). Invalid tool names will cause the server to fail at startup with an error message
- When tools are renamed, old names are preserved as aliases for backward compatibility. See [Tool Renaming](docs/tool-renaming.md) for details.
Expand Down Expand Up @@ -1462,29 +1454,6 @@ The following sets of tools are available:

</details>

## Dynamic Tool Discovery

**Note**: This feature is currently in beta and is not available in the Remote GitHub MCP Server. Please test it out and let us know if you encounter any issues.

Instead of starting with all tools enabled, you can turn on dynamic toolset discovery. Dynamic toolsets allow the MCP host to list and enable toolsets in response to a user prompt. This should help to avoid situations where the model gets confused by the sheer number of tools available.

### Using Dynamic Tool Discovery

When using the binary, you can pass the `--dynamic-toolsets` flag.

```bash
./github-mcp-server --dynamic-toolsets
```

When using Docker, you can pass the toolsets as environment variables:

```bash
docker run -i --rm \
-e GITHUB_PERSONAL_ACCESS_TOKEN=<your-token> \
-e GITHUB_DYNAMIC_TOOLSETS=1 \
ghcr.io/github/github-mcp-server
```

## Read-Only Mode

To run the server in read-only mode, you can use the `--read-only` flag. This will only offer read-only tools, preventing any modifications to repositories, issues, pull requests, etc.
Expand Down
8 changes: 4 additions & 4 deletions cmd/github-mcp-server/generate_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ func generateToolsetsDoc(i *inventory.Inventory) string {
fmt.Fprintf(&buf, "| %s | `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |\n", contextIcon)

// AvailableToolsets() returns toolsets that have tools, sorted by ID
// Exclude context (custom description above) and dynamic (internal only)
for _, ts := range i.AvailableToolsets("context", "dynamic") {
// Exclude context (custom description above)
for _, ts := range i.AvailableToolsets("context") {
icon := octiconImg(ts.Icon)
fmt.Fprintf(&buf, "| %s | `%s` | %s |\n", icon, ts.ID, ts.Description)
}
Expand Down Expand Up @@ -346,8 +346,8 @@ func generateRemoteToolsetsDoc() string {
fmt.Fprintf(&buf, "| %s<br>`all` | All available GitHub MCP tools | https://api.githubcopilot.com/mcp/ | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%%7B%%22type%%22%%3A%%20%%22http%%22%%2C%%22url%%22%%3A%%20%%22https%%3A%%2F%%2Fapi.githubcopilot.com%%2Fmcp%%2F%%22%%7D) | [read-only](https://api.githubcopilot.com/mcp/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%%7B%%22type%%22%%3A%%20%%22http%%22%%2C%%22url%%22%%3A%%20%%22https%%3A%%2F%%2Fapi.githubcopilot.com%%2Fmcp%%2Freadonly%%22%%7D) |\n", allIcon)

// AvailableToolsets() returns toolsets that have tools, sorted by ID
// Exclude context (handled separately) and dynamic (internal only)
for _, ts := range r.AvailableToolsets("context", "dynamic") {
// Exclude context (handled separately)
for _, ts := range r.AvailableToolsets("context") {
idStr := string(ts.ID)

apiURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s", idStr)
Expand Down
4 changes: 0 additions & 4 deletions cmd/github-mcp-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ var (
EnabledToolsets: enabledToolsets,
EnabledTools: enabledTools,
EnabledFeatures: enabledFeatures,
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
ReadOnly: viper.GetBool("read-only"),
ExportTranslations: viper.GetBool("export-translations"),
EnableCommandLogging: viper.GetBool("enable-command-logging"),
Expand Down Expand Up @@ -144,7 +143,6 @@ var (
ReadOnly: viper.GetBool("read-only"),
EnabledToolsets: enabledToolsets,
EnabledTools: enabledTools,
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
ExcludeTools: excludeTools,
InsidersMode: viper.GetBool("insiders"),
}
Expand All @@ -165,7 +163,6 @@ func init() {
rootCmd.PersistentFlags().StringSlice("tools", nil, "Comma-separated list of specific tools to enable")
rootCmd.PersistentFlags().StringSlice("exclude-tools", nil, "Comma-separated list of tool names to disable regardless of other settings")
rootCmd.PersistentFlags().StringSlice("features", nil, "Comma-separated list of feature flags to enable")
rootCmd.PersistentFlags().Bool("dynamic-toolsets", false, "Enable dynamic toolsets")
rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server to read-only operations")
rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
Expand All @@ -187,7 +184,6 @@ func init() {
_ = viper.BindPFlag("tools", rootCmd.PersistentFlags().Lookup("tools"))
_ = viper.BindPFlag("exclude_tools", rootCmd.PersistentFlags().Lookup("exclude-tools"))
_ = viper.BindPFlag("features", rootCmd.PersistentFlags().Lookup("features"))
_ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets"))
_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
_ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
Expand Down
1 change: 0 additions & 1 deletion docs/installation-guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,5 @@ If you encounter issues:
After installation, you may want to explore:
- **Toolsets**: Enable/disable specific GitHub API capabilities
- **Read-Only Mode**: Restrict to read-only operations
- **Dynamic Tool Discovery**: Enable tools on-demand
- **Lockdown Mode**: Hide public issue details created by users without push access

57 changes: 1 addition & 56 deletions docs/server-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ We currently support the following ways in which the GitHub MCP Server can be co
| Individual Tools | `X-MCP-Tools` header | `--tools` flag or `GITHUB_TOOLS` env var |
| Exclude Tools | `X-MCP-Exclude-Tools` header | `--exclude-tools` flag or `GITHUB_EXCLUDE_TOOLS` env var |
| Read-Only Mode | `X-MCP-Readonly` header or `/readonly` URL | `--read-only` flag or `GITHUB_READ_ONLY` env var |
| Dynamic Mode | Not available | `--dynamic-toolsets` flag or `GITHUB_DYNAMIC_TOOLSETS` env var |
| Lockdown Mode | `X-MCP-Lockdown` header | `--lockdown-mode` flag or `GITHUB_LOCKDOWN_MODE` env var |
| Insiders Mode | `X-MCP-Insiders` header or `/insiders` URL | `--insiders` flag or `GITHUB_INSIDERS` env var |
| Feature Flags | `X-MCP-Features` header | `--features` flag |
Expand All @@ -24,7 +23,7 @@ We currently support the following ways in which the GitHub MCP Server can be co

## How Configuration Works

All configuration options are **composable**: you can combine toolsets, individual tools, excluded tools, dynamic discovery, read-only mode and lockdown mode in any way that suits your workflow.
All configuration options are **composable**: you can combine toolsets, individual tools, excluded tools, read-only mode and lockdown mode in any way that suits your workflow.

Note: **read-only** mode acts as a strict security filter that takes precedence over any other configuration, by disabling write tools even when explicitly requested.

Expand Down Expand Up @@ -287,59 +286,6 @@ When active, this mode will disable all tools that are not read-only even if the

---

### Dynamic Discovery (Local Only)

**Best for:** Letting the LLM discover and enable toolsets as needed.

Starts with only discovery tools (`enable_toolset`, `list_available_toolsets`, `get_toolset_tools`), then expands on demand.

<table>
<tr><th>Local Server Only</th></tr>
<tr valign="top">
<td>

```json
{
"type": "stdio",
"command": "go",
"args": [
"run",
"./cmd/github-mcp-server",
"stdio",
"--dynamic-toolsets"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}"
}
}
```

**With some tools pre-enabled:**
```json
{
"type": "stdio",
"command": "go",
"args": [
"run",
"./cmd/github-mcp-server",
"stdio",
"--dynamic-toolsets",
"--tools=get_me,search_code"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}"
}
}
```

</td>
</tr>
</table>

When both dynamic mode and specific tools are enabled in the server configuration, the server will start with the 3 dynamic tools + the specified tools.

---

### Lockdown Mode

**Best for:** Public repositories where you want to limit content from users without push access.
Expand Down Expand Up @@ -521,7 +467,6 @@ See [Scope Filtering](./scope-filtering.md) for details on how filtering works w
| Server fails to start | Invalid tool name in `--tools` or `X-MCP-Tools` | Check tool name spelling; use exact names from [Tools list](../README.md#tools) |
| Write tools not working | Read-only mode enabled | Remove `--read-only` flag or `X-MCP-Readonly` header |
| Tools missing | Toolset not enabled | Add the required toolset or specific tool |
| Dynamic tools not available | Using remote server | Dynamic mode is available in the local MCP server only |

---

Expand Down
1 change: 0 additions & 1 deletion docs/toolsets-and-icons.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ icons := octicons.Icons("repo")
| Labels | `tag` |
| Stargazers | `star` |
| Notifications | `bell` |
| Dynamic | `tools` |
| Copilot | `copilot` |
| Support Search | `book` |

Expand Down
9 changes: 2 additions & 7 deletions internal/ghmcp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func NewStdioMCPServer(ctx context.Context, cfg github.MCPServerConfig) (*mcp.Se
inventoryBuilder := github.NewInventory(cfg.Translator).
WithDeprecatedAliases(github.DeprecatedToolAliases).
WithReadOnly(cfg.ReadOnly).
WithToolsets(github.ResolvedEnabledToolsets(cfg.DynamicToolsets, cfg.EnabledToolsets, cfg.EnabledTools)).
WithToolsets(github.ResolvedEnabledToolsets(cfg.EnabledToolsets, cfg.EnabledTools)).
WithTools(github.CleanTools(cfg.EnabledTools)).
WithExcludeTools(cfg.ExcludeTools).
WithServerInstructions().
Expand Down Expand Up @@ -210,10 +210,6 @@ type StdioServerConfig struct {
// Items with FeatureFlagEnable matching an entry in this list will be available
EnabledFeatures []string

// Whether to enable dynamic toolsets
// See: https://github.com/github/github-mcp-server?tab=readme-ov-file#dynamic-tool-discovery
DynamicToolsets bool

// ReadOnly indicates if we should only register read-only tools
ReadOnly bool

Expand Down Expand Up @@ -267,7 +263,7 @@ func RunStdioServer(cfg StdioServerConfig) error {
slogHandler = slog.NewTextHandler(logOutput, &slog.HandlerOptions{Level: slog.LevelInfo})
}
logger := slog.New(slogHandler)
logger.Info("starting server", "version", cfg.Version, "host", cfg.Host, "dynamicToolsets", cfg.DynamicToolsets, "readOnly", cfg.ReadOnly, "lockdownEnabled", cfg.LockdownMode)
logger.Info("starting server", "version", cfg.Version, "host", cfg.Host, "readOnly", cfg.ReadOnly, "lockdownEnabled", cfg.LockdownMode)

// Fetch token scopes for scope-based tool filtering (PAT tokens only)
// Only classic PATs (ghp_ prefix) return OAuth scopes via X-OAuth-Scopes header.
Expand All @@ -292,7 +288,6 @@ func RunStdioServer(cfg StdioServerConfig) error {
EnabledToolsets: cfg.EnabledToolsets,
EnabledTools: cfg.EnabledTools,
EnabledFeatures: cfg.EnabledFeatures,
DynamicToolsets: cfg.DynamicToolsets,
ReadOnly: cfg.ReadOnly,
Translator: t,
ContentWindowSize: cfg.ContentWindowSize,
Expand Down
Loading
Loading