diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9bbf555e1..6c16cd27d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -40,6 +40,7 @@ These are one time installations required to be able to test your changes locall
- Update snapshots and run tests: `UPDATE_TOOLSNAPS=true go test ./...`
- Update readme documentation: `script/generate-docs`
- If renaming a tool, add a deprecation alias (see [Tool Renaming Guide](docs/tool-renaming.md))
+ - For toolset and icon configuration, see [Toolsets and Icons Guide](docs/toolsets-and-icons.md)
6. Push to your fork and [submit a pull request][pr] targeting the `main` branch
7. Pat yourself on the back and wait for your pull request to be reviewed and merged.
diff --git a/README.md b/README.md
index 0e96526f7..4071874bd 100644
--- a/README.md
+++ b/README.md
@@ -453,26 +453,26 @@ GITHUB_TOOLSETS="default,stargazers" ./github-mcp-server
The following sets of tools are available:
-| Toolset | Description |
-| ----------------------- | ------------------------------------------------------------- |
-| `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |
-| `actions` | GitHub Actions workflows and CI/CD operations |
-| `code_security` | Code security related tools, such as GitHub Code Scanning |
-| `dependabot` | Dependabot tools |
-| `discussions` | GitHub Discussions related tools |
-| `gists` | GitHub Gist related tools |
-| `git` | GitHub Git API related tools for low-level Git operations |
-| `issues` | GitHub Issues related tools |
-| `labels` | GitHub Labels related tools |
-| `notifications` | GitHub Notifications related tools |
-| `orgs` | GitHub Organization related tools |
-| `projects` | GitHub Projects related tools |
-| `pull_requests` | GitHub Pull Request related tools |
-| `repos` | GitHub Repository related tools |
-| `secret_protection` | Secret protection related tools, such as GitHub Secret Scanning |
-| `security_advisories` | Security advisories related tools |
-| `stargazers` | GitHub Stargazers related tools |
-| `users` | GitHub User related tools |
+| | Toolset | Description |
+| --- | ----------------------- | ------------------------------------------------------------- |
+|
| `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |
+|
| `actions` | GitHub Actions workflows and CI/CD operations |
+|
| `code_security` | Code security related tools, such as GitHub Code Scanning |
+|
| `dependabot` | Dependabot tools |
+|
| `discussions` | GitHub Discussions related tools |
+|
| `gists` | GitHub Gist related tools |
+|
| `git` | GitHub Git API related tools for low-level Git operations |
+|
| `issues` | GitHub Issues related tools |
+|
| `labels` | GitHub Labels related tools |
+|
| `notifications` | GitHub Notifications related tools |
+|
| `orgs` | GitHub Organization related tools |
+|
| `projects` | GitHub Projects related tools |
+|
| `pull_requests` | GitHub Pull Request related tools |
+|
| `repos` | GitHub Repository related tools |
+|
| `secret_protection` | Secret protection related tools, such as GitHub Secret Scanning |
+|
| `security_advisories` | Security advisories related tools |
+|
| `stargazers` | GitHub Stargazers related tools |
+|
| `users` | GitHub User related tools |
### Additional Toolsets in Remote GitHub MCP Server
@@ -488,7 +488,7 @@ The following sets of tools are available:
-Actions
+
Actions
- **cancel_workflow_run** - Cancel workflow run
- `owner`: Repository owner (string, required)
@@ -582,7 +582,7 @@ The following sets of tools are available:
-Code Security
+
Code Security
- **get_code_scanning_alert** - Get code scanning alert
- `alertNumber`: The number of the alert. (number, required)
@@ -601,7 +601,7 @@ The following sets of tools are available:
-Context
+
Context
- **get_me** - Get my user profile
- No parameters required
@@ -617,7 +617,7 @@ The following sets of tools are available:
-Dependabot
+
Dependabot
- **get_dependabot_alert** - Get dependabot alert
- `alertNumber`: The number of the alert. (number, required)
@@ -634,7 +634,7 @@ The following sets of tools are available:
-Discussions
+
Discussions
- **get_discussion** - Get discussion
- `discussionNumber`: Discussion Number (number, required)
@@ -665,7 +665,7 @@ The following sets of tools are available:
-Gists
+
Gists
- **create_gist** - Create Gist
- `content`: Content for simple single-file gist creation (string, required)
@@ -692,7 +692,7 @@ The following sets of tools are available:
-Git
+
Git
- **get_repository_tree** - Get repository tree
- `owner`: Repository owner (username or organization) (string, required)
@@ -705,7 +705,7 @@ The following sets of tools are available:
-Issues
+
Issues
- **add_issue_comment** - Add comment to issue
- `body`: Comment content (string, required)
@@ -798,7 +798,7 @@ The following sets of tools are available:
-Labels
+
Labels
- **get_label** - Get a specific label from a repository.
- `name`: Label name. (string, required)
@@ -822,7 +822,7 @@ The following sets of tools are available:
-Notifications
+
Notifications
- **dismiss_notification** - Dismiss notification
- `state`: The new state of the notification (read/done) (string, required)
@@ -858,7 +858,7 @@ The following sets of tools are available:
-Organizations
+
Organizations
- **search_orgs** - Search organizations
- `order`: Sort order (string, optional)
@@ -871,7 +871,7 @@ The following sets of tools are available:
-Projects
+
Projects
- **add_project_item** - Add project item
- `item_id`: The numeric ID of the issue or pull request to add to the project. (number, required)
@@ -941,7 +941,7 @@ The following sets of tools are available:
-Pull Requests
+
Pull Requests
- **add_comment_to_pending_review** - Add review comment to the requester's latest pending pull request review
- `body`: The text of the review comment (string, required)
@@ -1046,7 +1046,7 @@ The following sets of tools are available:
-Repositories
+
Repositories
- **create_branch** - Create branch
- `branch`: Name for new branch (string, required)
@@ -1163,7 +1163,7 @@ The following sets of tools are available:
-Secret Protection
+
Secret Protection
- **get_secret_scanning_alert** - Get secret scanning alert
- `alertNumber`: The number of the alert. (number, required)
@@ -1181,7 +1181,7 @@ The following sets of tools are available:
-Security Advisories
+
Security Advisories
- **get_global_security_advisory** - Get a global security advisory
- `ghsaId`: GitHub Security Advisory ID (format: GHSA-xxxx-xxxx-xxxx). (string, required)
@@ -1216,7 +1216,7 @@ The following sets of tools are available:
-Stargazers
+
Stargazers
- **list_starred_repositories** - List starred repositories
- `direction`: The direction to sort the results by. (string, optional)
@@ -1237,7 +1237,7 @@ The following sets of tools are available:
-Users
+
Users
- **search_users** - Search users
- `order`: Sort order (string, optional)
diff --git a/cmd/github-mcp-server/generate_docs.go b/cmd/github-mcp-server/generate_docs.go
index a206c9c7e..f822d69f2 100644
--- a/cmd/github-mcp-server/generate_docs.go
+++ b/cmd/github-mcp-server/generate_docs.go
@@ -101,23 +101,52 @@ func generateRemoteServerDocs(docsPath string) error {
return err
}
+ // Also generate remote-only toolsets section
+ remoteOnlyDoc := generateRemoteOnlyToolsetsDoc()
+ updatedContent, err = replaceSection(updatedContent, "START AUTOMATED REMOTE TOOLSETS", "END AUTOMATED REMOTE TOOLSETS", remoteOnlyDoc)
+ if err != nil {
+ return err
+ }
+
return os.WriteFile(docsPath, []byte(updatedContent), 0600) //#nosec G306
}
+// octiconImg returns an img tag for an Octicon that works with GitHub's light/dark theme.
+// Uses picture element with prefers-color-scheme for automatic theme switching.
+// References icons from the repo's pkg/octicons/icons directory.
+// Optional pathPrefix for files in subdirectories (e.g., "../" for docs/).
+func octiconImg(name string, pathPrefix ...string) string {
+ if name == "" {
+ return ""
+ }
+ prefix := ""
+ if len(pathPrefix) > 0 {
+ prefix = pathPrefix[0]
+ }
+ // Use picture element with media queries for light/dark mode support
+ // GitHub renders these correctly in markdown
+ lightIcon := fmt.Sprintf("%spkg/octicons/icons/%s-light.png", prefix, name)
+ darkIcon := fmt.Sprintf("%spkg/octicons/icons/%s-dark.png", prefix, name)
+ return fmt.Sprintf(`
`, darkIcon, lightIcon, lightIcon, name)
+}
+
func generateToolsetsDoc(i *inventory.Inventory) string {
var buf strings.Builder
- // Add table header and separator
- buf.WriteString("| Toolset | Description |\n")
- buf.WriteString("| ----------------------- | ------------------------------------------------------------- |\n")
+ // Add table header and separator (with icon column)
+ buf.WriteString("| | Toolset | Description |\n")
+ buf.WriteString("| --- | ----------------------- | ------------------------------------------------------------- |\n")
// Add the context toolset row with custom description (strongly recommended)
- buf.WriteString("| `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |\n")
+ // Get context toolset for its icon
+ contextIcon := octiconImg("person")
+ 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") {
- fmt.Fprintf(&buf, "| `%s` | %s |\n", ts.ID, ts.Description)
+ icon := octiconImg(ts.Icon)
+ fmt.Fprintf(&buf, "| %s | `%s` | %s |\n", icon, ts.ID, ts.Description)
}
return strings.TrimSuffix(buf.String(), "\n")
@@ -134,6 +163,7 @@ func generateToolsDoc(r *inventory.Inventory) string {
var buf strings.Builder
var toolBuf strings.Builder
var currentToolsetID inventory.ToolsetID
+ var currentToolsetIcon string
firstSection := true
writeSection := func() {
@@ -145,7 +175,11 @@ func generateToolsDoc(r *inventory.Inventory) string {
}
firstSection = false
sectionName := formatToolsetName(string(currentToolsetID))
- fmt.Fprintf(&buf, "\n\n%s
\n\n%s\n\n ", sectionName, strings.TrimSuffix(toolBuf.String(), "\n\n"))
+ icon := octiconImg(currentToolsetIcon)
+ if icon != "" {
+ icon += " "
+ }
+ fmt.Fprintf(&buf, "\n\n%s%s
\n\n%s\n\n ", icon, sectionName, strings.TrimSuffix(toolBuf.String(), "\n\n"))
toolBuf.Reset()
}
@@ -154,6 +188,7 @@ func generateToolsDoc(r *inventory.Inventory) string {
if tool.Toolset.ID != currentToolsetID {
writeSection()
currentToolsetID = tool.Toolset.ID
+ currentToolsetIcon = tool.Toolset.Icon
}
writeToolDoc(&toolBuf, tool.Tool)
toolBuf.WriteString("\n\n")
@@ -190,7 +225,7 @@ func formatToolsetName(name string) string {
}
func writeToolDoc(buf *strings.Builder, tool mcp.Tool) {
- // Tool name only (using annotation name instead of verbose description)
+ // Tool name (no icon - section header already has the toolset icon)
fmt.Fprintf(buf, "- **%s** - %s\n", tool.Name, tool.Annotations.Title)
// Parameters
@@ -302,12 +337,13 @@ func generateRemoteToolsetsDoc() string {
// Build inventory - stateless
r := github.NewInventory(t).Build()
- // Generate table header
- buf.WriteString("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n")
- buf.WriteString("|----------------|--------------------------------------------------|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n")
+ // Generate table header (icon is combined with Name column)
+ buf.WriteString("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n")
+ buf.WriteString("| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |\n")
// Add "all" toolset first (special case)
- buf.WriteString("| 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 := octiconImg("apps", "../")
+ fmt.Fprintf(&buf, "| %s
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)
@@ -329,12 +365,14 @@ func generateRemoteToolsetsDoc() string {
installLink := fmt.Sprintf("[Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, installConfig)
readonlyInstallLink := fmt.Sprintf("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, readonlyConfig)
- fmt.Fprintf(&buf, "| %-14s | %-48s | %-53s | %-218s | %-110s | %-288s |\n",
+ icon := octiconImg(ts.Icon, "../")
+ fmt.Fprintf(&buf, "| %s
%s | %s | %s | %s | [read-only](%s) | %s |\n",
+ icon,
formattedName,
ts.Description,
apiURL,
installLink,
- fmt.Sprintf("[read-only](%s)", readonlyURL),
+ readonlyURL,
readonlyInstallLink,
)
}
@@ -342,6 +380,46 @@ func generateRemoteToolsetsDoc() string {
return strings.TrimSuffix(buf.String(), "\n")
}
+func generateRemoteOnlyToolsetsDoc() string {
+ var buf strings.Builder
+
+ // Generate table header (icon is combined with Name column)
+ buf.WriteString("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n")
+ buf.WriteString("| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |\n")
+
+ // Use RemoteOnlyToolsets from github package
+ for _, ts := range github.RemoteOnlyToolsets() {
+ idStr := string(ts.ID)
+
+ formattedName := formatToolsetName(idStr)
+ apiURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s", idStr)
+ readonlyURL := fmt.Sprintf("https://api.githubcopilot.com/mcp/x/%s/readonly", idStr)
+
+ // Create install config JSON (URL encoded)
+ installConfig := url.QueryEscape(fmt.Sprintf(`{"type": "http","url": "%s"}`, apiURL))
+ readonlyConfig := url.QueryEscape(fmt.Sprintf(`{"type": "http","url": "%s"}`, readonlyURL))
+
+ // Fix URL encoding to use %20 instead of + for spaces
+ installConfig = strings.ReplaceAll(installConfig, "+", "%20")
+ readonlyConfig = strings.ReplaceAll(readonlyConfig, "+", "%20")
+
+ installLink := fmt.Sprintf("[Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, installConfig)
+ readonlyInstallLink := fmt.Sprintf("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)", idStr, readonlyConfig)
+
+ icon := octiconImg(ts.Icon, "../")
+ fmt.Fprintf(&buf, "| %s
%s | %s | %s | %s | [read-only](%s) | %s |\n",
+ icon,
+ formattedName,
+ ts.Description,
+ apiURL,
+ installLink,
+ readonlyURL,
+ readonlyInstallLink,
+ )
+ }
+
+ return strings.TrimSuffix(buf.String(), "\n")
+}
func generateDeprecatedAliasesDocs(docsPath string) error {
// Read the current file
content, err := os.ReadFile(docsPath) //#nosec G304
diff --git a/docs/remote-server.md b/docs/remote-server.md
index 53fe36127..d7d0f72b1 100644
--- a/docs/remote-server.md
+++ b/docs/remote-server.md
@@ -17,37 +17,39 @@ The remote server has [additional tools](#toolsets-only-available-in-the-remote-
Below is a table of available toolsets for the remote GitHub MCP Server. Each toolset is provided as a distinct URL so you can mix and match to create the perfect combination of tools for your use-case. Add `/readonly` to the end of any URL to restrict the tools in the toolset to only those that enable read access. We also provide the option to use [headers](#headers) instead.
-| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |
-|----------------|--------------------------------------------------|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| 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) |
-| Actions | GitHub Actions workflows and CI/CD operations | https://api.githubcopilot.com/mcp/x/actions | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/actions/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%2Freadonly%22%7D) |
-| Code Security | Code security related tools, such as GitHub Code Scanning | https://api.githubcopilot.com/mcp/x/code_security | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/code_security/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%2Freadonly%22%7D) |
-| Dependabot | Dependabot tools | https://api.githubcopilot.com/mcp/x/dependabot | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/dependabot/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%2Freadonly%22%7D) |
-| Discussions | GitHub Discussions related tools | https://api.githubcopilot.com/mcp/x/discussions | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-discussions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdiscussions%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/discussions/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-discussions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdiscussions%2Freadonly%22%7D) |
-| Gists | GitHub Gist related tools | https://api.githubcopilot.com/mcp/x/gists | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-gists&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgists%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/gists/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-gists&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgists%2Freadonly%22%7D) |
-| Git | GitHub Git API related tools for low-level Git operations | https://api.githubcopilot.com/mcp/x/git | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-git&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgit%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/git/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-git&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgit%2Freadonly%22%7D) |
-| Issues | GitHub Issues related tools | https://api.githubcopilot.com/mcp/x/issues | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-issues&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fissues%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/issues/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-issues&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fissues%2Freadonly%22%7D) |
-| Labels | GitHub Labels related tools | https://api.githubcopilot.com/mcp/x/labels | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-labels&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Flabels%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/labels/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-labels&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Flabels%2Freadonly%22%7D) |
-| Notifications | GitHub Notifications related tools | https://api.githubcopilot.com/mcp/x/notifications | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-notifications&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fnotifications%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/notifications/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-notifications&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fnotifications%2Freadonly%22%7D) |
-| Organizations | GitHub Organization related tools | https://api.githubcopilot.com/mcp/x/orgs | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/orgs/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%2Freadonly%22%7D) |
-| Projects | GitHub Projects related tools | https://api.githubcopilot.com/mcp/x/projects | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/projects/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%2Freadonly%22%7D) |
-| Pull Requests | GitHub Pull Request related tools | https://api.githubcopilot.com/mcp/x/pull_requests | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/pull_requests/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%2Freadonly%22%7D) |
-| Repositories | GitHub Repository related tools | https://api.githubcopilot.com/mcp/x/repos | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/repos/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%2Freadonly%22%7D) |
-| Secret Protection | Secret protection related tools, such as GitHub Secret Scanning | https://api.githubcopilot.com/mcp/x/secret_protection | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/secret_protection/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%2Freadonly%22%7D) |
-| Security Advisories | Security advisories related tools | https://api.githubcopilot.com/mcp/x/security_advisories | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/security_advisories/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%2Freadonly%22%7D) |
-| Stargazers | GitHub Stargazers related tools | https://api.githubcopilot.com/mcp/x/stargazers | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-stargazers&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fstargazers%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/stargazers/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-stargazers&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fstargazers%2Freadonly%22%7D) |
-| Users | GitHub User related tools | https://api.githubcopilot.com/mcp/x/users | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-users&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fusers%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/users/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-users&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fusers%2Freadonly%22%7D) |
+| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |
+| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |
+| 
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) |
+| 
Actions | GitHub Actions workflows and CI/CD operations | https://api.githubcopilot.com/mcp/x/actions | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/actions/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-actions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Factions%2Freadonly%22%7D) |
+| 
Code Security | Code security related tools, such as GitHub Code Scanning | https://api.githubcopilot.com/mcp/x/code_security | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/code_security/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-code_security&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fcode_security%2Freadonly%22%7D) |
+| 
Dependabot | Dependabot tools | https://api.githubcopilot.com/mcp/x/dependabot | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/dependabot/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-dependabot&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdependabot%2Freadonly%22%7D) |
+| 
Discussions | GitHub Discussions related tools | https://api.githubcopilot.com/mcp/x/discussions | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-discussions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdiscussions%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/discussions/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-discussions&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fdiscussions%2Freadonly%22%7D) |
+| 
Gists | GitHub Gist related tools | https://api.githubcopilot.com/mcp/x/gists | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-gists&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgists%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/gists/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-gists&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgists%2Freadonly%22%7D) |
+| 
Git | GitHub Git API related tools for low-level Git operations | https://api.githubcopilot.com/mcp/x/git | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-git&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgit%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/git/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-git&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fgit%2Freadonly%22%7D) |
+| 
Issues | GitHub Issues related tools | https://api.githubcopilot.com/mcp/x/issues | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-issues&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fissues%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/issues/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-issues&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fissues%2Freadonly%22%7D) |
+| 
Labels | GitHub Labels related tools | https://api.githubcopilot.com/mcp/x/labels | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-labels&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Flabels%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/labels/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-labels&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Flabels%2Freadonly%22%7D) |
+| 
Notifications | GitHub Notifications related tools | https://api.githubcopilot.com/mcp/x/notifications | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-notifications&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fnotifications%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/notifications/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-notifications&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fnotifications%2Freadonly%22%7D) |
+| 
Organizations | GitHub Organization related tools | https://api.githubcopilot.com/mcp/x/orgs | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/orgs/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-orgs&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Forgs%2Freadonly%22%7D) |
+| 
Projects | GitHub Projects related tools | https://api.githubcopilot.com/mcp/x/projects | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/projects/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-projects&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fprojects%2Freadonly%22%7D) |
+| 
Pull Requests | GitHub Pull Request related tools | https://api.githubcopilot.com/mcp/x/pull_requests | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/pull_requests/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-pull_requests&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fpull_requests%2Freadonly%22%7D) |
+| 
Repositories | GitHub Repository related tools | https://api.githubcopilot.com/mcp/x/repos | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/repos/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-repos&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Frepos%2Freadonly%22%7D) |
+| 
Secret Protection | Secret protection related tools, such as GitHub Secret Scanning | https://api.githubcopilot.com/mcp/x/secret_protection | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/secret_protection/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-secret_protection&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecret_protection%2Freadonly%22%7D) |
+| 
Security Advisories | Security advisories related tools | https://api.githubcopilot.com/mcp/x/security_advisories | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/security_advisories/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-security_advisories&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fsecurity_advisories%2Freadonly%22%7D) |
+| 
Stargazers | GitHub Stargazers related tools | https://api.githubcopilot.com/mcp/x/stargazers | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-stargazers&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fstargazers%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/stargazers/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-stargazers&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fstargazers%2Freadonly%22%7D) |
+| 
Users | GitHub User related tools | https://api.githubcopilot.com/mcp/x/users | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-users&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fusers%22%7D) | [read-only](https://api.githubcopilot.com/mcp/x/users/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-users&config=%7B%22type%22%3A%20%22http%22%2C%22url%22%3A%20%22https%3A%2F%2Fapi.githubcopilot.com%2Fmcp%2Fx%2Fusers%2Freadonly%22%7D) |
### Additional _Remote_ Server Toolsets
These toolsets are only available in the remote GitHub MCP Server and are not included in the local MCP server.
-| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |
-| -------------------- | --------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| 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) |
-| 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) |
-| 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) |
+
+| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |
+| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |
+| 
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) |
+| 
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) |
+| 
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-github_support_docs_search&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-github_support_docs_search&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) |
+
### Optional Headers
diff --git a/docs/toolsets-and-icons.md b/docs/toolsets-and-icons.md
new file mode 100644
index 000000000..9c26b4aa1
--- /dev/null
+++ b/docs/toolsets-and-icons.md
@@ -0,0 +1,201 @@
+# Toolsets and Icons
+
+This document explains how to work with toolsets and icons in the GitHub MCP Server.
+
+## Toolset Overview
+
+Toolsets are logical groupings of related tools. Each toolset has metadata defined in `pkg/github/tools.go`:
+
+```go
+ToolsetMetadataRepos = inventory.ToolsetMetadata{
+ ID: "repos",
+ Description: "GitHub Repository related tools",
+ Default: true,
+ Icon: "repo",
+}
+```
+
+### Toolset Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `ID` | `ToolsetID` | Unique identifier used in URLs and CLI flags (e.g., `repos`, `issues`) |
+| `Description` | `string` | Human-readable description shown in documentation |
+| `Default` | `bool` | Whether this toolset is enabled by default |
+| `Icon` | `string` | Octicon name for visual representation in MCP clients |
+
+## Adding Icons to Toolsets
+
+Icons help users quickly identify toolsets in MCP-compatible clients. We use [Primer Octicons](https://primer.style/foundations/icons) for all icons.
+
+### Step 1: Choose an Octicon
+
+Browse the [Octicon gallery](https://primer.style/foundations/icons) and select an appropriate icon. Use the base name without size suffix (e.g., `repo` not `repo-16`).
+
+### Step 2: Add Icon to Required Icons List
+
+Icons are defined in `pkg/octicons/required_icons.txt`, which is the single source of truth for which icons should be embedded:
+
+```
+# Required icons for the GitHub MCP Server
+# Add new icons below (one per line)
+repo
+issue-opened
+git-pull-request
+your-new-icon # Add your icon here
+```
+
+### Step 3: Fetch the Icon Files
+
+Run the fetch-icons script to download and convert the icon:
+
+```bash
+# Fetch a specific icon
+script/fetch-icons your-new-icon
+
+# Or fetch all required icons
+script/fetch-icons
+```
+
+This script:
+- Downloads the 24px SVG from [Primer Octicons](https://github.com/primer/octicons)
+- Converts to PNG with light theme (dark icons for light backgrounds)
+- Converts to PNG with dark theme (white icons for dark backgrounds)
+- Saves both variants to `pkg/octicons/icons/`
+
+**Requirements:** The script requires `rsvg-convert`:
+- Ubuntu/Debian: `sudo apt-get install librsvg2-bin`
+- macOS: `brew install librsvg`
+
+### Step 4: Update the Toolset Metadata
+
+Add or update the `Icon` field in the toolset definition:
+
+```go
+// In pkg/github/tools.go
+ToolsetMetadataRepos = inventory.ToolsetMetadata{
+ ID: "repos",
+ Description: "GitHub Repository related tools",
+ Default: true,
+ Icon: "repo", // Add this line
+}
+```
+
+### Step 5: Regenerate Documentation
+
+Run the documentation generator to update all markdown files:
+
+```bash
+go run ./cmd/github-mcp-server generate-docs
+```
+
+This updates icons in:
+- `README.md` - Toolsets table and tool section headers
+- `docs/remote-server.md` - Remote toolsets table
+
+## Remote-Only Toolsets
+
+Some toolsets are only available in the remote GitHub MCP Server (hosted at `api.githubcopilot.com`). These are defined in `pkg/github/tools.go` with their icons, but are not registered with the local server:
+
+```go
+// Remote-only toolsets
+ToolsetMetadataCopilot = inventory.ToolsetMetadata{
+ ID: "copilot",
+ Description: "Copilot related tools",
+ Icon: "copilot",
+}
+```
+
+The `RemoteOnlyToolsets()` function returns the list of these toolsets for documentation generation.
+
+To add a new remote-only toolset:
+
+1. Add the metadata definition in `pkg/github/tools.go`
+2. Add it to the slice returned by `RemoteOnlyToolsets()`
+3. Regenerate documentation
+
+## Tool Icon Inheritance
+
+Individual tools inherit icons from their parent toolset. When a tool is registered with a toolset, its icons are automatically set:
+
+```go
+// In pkg/inventory/server_tool.go
+toolCopy.Icons = tool.Toolset.Icons()
+```
+
+This means you only need to set the icon once on the toolset, and all tools in that toolset will display the same icon.
+
+## How Icons Work in MCP
+
+The MCP protocol supports tool icons via the `icons` field. We provide icons in two formats:
+
+1. **Data URIs** - Base64-encoded PNG images embedded in the tool definition
+2. **Light/Dark variants** - Both theme variants are provided for proper display
+
+The `octicons.Icons()` function generates the MCP-compatible icon objects:
+
+```go
+// Returns []mcp.Icon with both light and dark variants
+icons := octicons.Icons("repo")
+```
+
+## Existing Toolset Icons
+
+| Toolset | Octicon Name |
+|---------|--------------|
+| Context | `person` |
+| Repositories | `repo` |
+| Issues | `issue-opened` |
+| Pull Requests | `git-pull-request` |
+| Git | `git-branch` |
+| Users | `people` |
+| Organizations | `organization` |
+| Actions | `workflow` |
+| Code Security | `codescan` |
+| Secret Protection | `shield-lock` |
+| Dependabot | `dependabot` |
+| Discussions | `comment-discussion` |
+| Gists | `logo-gist` |
+| Security Advisories | `shield` |
+| Projects | `project` |
+| Labels | `tag` |
+| Stargazers | `star` |
+| Notifications | `bell` |
+| Dynamic | `tools` |
+| Copilot | `copilot` |
+| Support Search | `book` |
+
+## Troubleshooting
+
+### Icons not appearing in documentation
+
+1. Ensure PNG files exist in `pkg/octicons/icons/` with `-light.png` and `-dark.png` suffixes
+2. Run `go run ./cmd/github-mcp-server generate-docs` to regenerate
+3. Check that the `Icon` field is set on the toolset metadata
+
+### Icons not appearing in MCP clients
+
+1. Verify the client supports MCP tool icons
+2. Check that the octicons package is properly generating base64 data URIs
+3. Ensure the icon name matches a file in `pkg/octicons/icons/`
+
+## CI Validation
+
+The following tests run in CI to catch icon issues early:
+
+### `pkg/octicons.TestEmbeddedIconsExist`
+
+Verifies that all icons listed in `pkg/octicons/required_icons.txt` have corresponding PNG files embedded.
+
+### `pkg/github.TestAllToolsetIconsExist`
+
+Verifies that all toolset `Icon` fields reference icons that are properly embedded.
+
+### `pkg/github.TestToolsetMetadataHasIcons`
+
+Ensures all toolsets have an `Icon` field set.
+
+If any of these tests fail:
+1. Add the missing icon to `pkg/octicons/required_icons.txt`
+2. Run `script/fetch-icons` to download the icon
+3. Commit the new icon files
diff --git a/go.mod b/go.mod
index 3276d7451..9423ce557 100644
--- a/go.mod
+++ b/go.mod
@@ -37,7 +37,7 @@ require (
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/google/go-querystring v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/modelcontextprotocol/go-sdk v1.1.0
+ github.com/modelcontextprotocol/go-sdk v1.2.0-pre.1
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
diff --git a/go.sum b/go.sum
index 528bd3796..fc0980ab1 100644
--- a/go.sum
+++ b/go.sum
@@ -17,6 +17,8 @@ github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrK
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
+github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -55,8 +57,8 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/migueleliasweb/go-github-mock v1.3.0 h1:2sVP9JEMB2ubQw1IKto3/fzF51oFC6eVWOOFDgQoq88=
github.com/migueleliasweb/go-github-mock v1.3.0/go.mod h1:ipQhV8fTcj/G6m7BKzin08GaJ/3B5/SonRAkgrk0zCY=
-github.com/modelcontextprotocol/go-sdk v1.1.0 h1:Qjayg53dnKC4UZ+792W21e4BpwEZBzwgRW6LrjLWSwA=
-github.com/modelcontextprotocol/go-sdk v1.1.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
+github.com/modelcontextprotocol/go-sdk v1.2.0-pre.1 h1:14+JrlEIFvUmbu5+iJzWPLk8CkpvegfKr42oXyjp3O4=
+github.com/modelcontextprotocol/go-sdk v1.2.0-pre.1/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021 h1:31Y+Yu373ymebRdJN1cWLLooHH8xAr0MhKTEJGV/87g=
github.com/muesli/cache2go v0.0.0-20221011235721-518229cd8021/go.mod h1:WERUkUryfUWlrHnFSO/BEUZ+7Ns8aZy7iVOGewxKzcc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
diff --git a/internal/ghmcp/server.go b/internal/ghmcp/server.go
index 0b5e41e3c..4842b2c64 100644
--- a/internal/ghmcp/server.go
+++ b/internal/ghmcp/server.go
@@ -179,9 +179,11 @@ func NewMCPServer(cfg MCPServerConfig) (*mcp.Server, error) {
// In dynamic mode, explicitly advertise capabilities since tools/resources/prompts
// may be enabled at runtime even if none are registered initially.
if cfg.DynamicToolsets {
- serverOpts.HasTools = true
- serverOpts.HasResources = true
- serverOpts.HasPrompts = true
+ serverOpts.Capabilities = &mcp.ServerCapabilities{
+ Tools: &mcp.ToolCapabilities{},
+ Resources: &mcp.ResourceCapabilities{},
+ Prompts: &mcp.PromptCapabilities{},
+ }
}
ghServer := github.NewServer(cfg.Version, serverOpts)
diff --git a/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap b/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap
index e250ca9c1..aff4aa597 100644
--- a/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap
+++ b/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap
@@ -26,5 +26,23 @@
}
}
},
- "name": "assign_copilot_to_issue"
+ "name": "assign_copilot_to_issue",
+ "icons": [
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "light"
+ },
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "dark"
+ }
+ ]
}
\ No newline at end of file
diff --git a/pkg/github/__toolsnaps__/fork_repository.snap b/pkg/github/__toolsnaps__/fork_repository.snap
index c195bd7d2..ad48688b1 100644
--- a/pkg/github/__toolsnaps__/fork_repository.snap
+++ b/pkg/github/__toolsnaps__/fork_repository.snap
@@ -24,5 +24,23 @@
}
}
},
- "name": "fork_repository"
+ "name": "fork_repository",
+ "icons": [
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACuElEQVRIibWTTUhUYRiFn/fOdYyoydQxk4LEGzN3RudaLYL+qRaBQYsIItoHCW37ISNbRwUFLWoRZBEt+4EIooKoTdZQ6TWaNIgouzJkuGhG731b6JTojDNBntX3ne+c97zfH8wzZCbREm9bZ4hsQvkeDvl3+/r6xuYqEIvFFgdSvRuDqCrPMu6bVyUDrITTjdI1jR8KBbrj/fs3Q8WLp5p9Qx4BzVOUInIm058+XdAY0ztH6RLhSpAza1RlI2jENzhfqntfjAugEdTYMFEtS0GvonrKslNrZwWIhDYDMh6Wo4ODvaMfB9LPFaMHZGvJ8xHdAlzPDLx+8Smd/pE39SggAptnB2gwDBD6ReJvhSCpMFyq/uSa/NFX5UMJgGCaxywMwiH/bi4wh0SCOy1x5waiCUF2gnSW3AByEfSSZTsPVXFF9CDC4ALx7xU0ocLA87x8tG7ZHRUShsheVMKInMy46culArIj317WRpd7KB2GsAl4bKoccN2330t5ALBsJ7ASTvecoun6hNNt2U5QbM0oRip8E6Wt0gCUFPC12FKoGFnX0BgBDtVGG3/W1qzqz2a/5IrpLGt9pLahvhPhCKrnsiPDT2dqZv1kgGQyGc4FZg+wr8I93F6y0DzY29s7XlHAnw7j7dswgg2oRCYZPTBluzk51VEwXmQG0k8qbGRuWHbqiWWn/qlY0Uv+n5j3gKKvaCaSyeSimrqms4hsB4kurW9c0bSs/pnneflyXrOcACCn5jWEPSr0AAgczvlVTVT+ykojFlvTZNmOWvHU8QJnJVInLNtR2163vJy/7B0EpjYAqBhugVMVF8A3goZy/rJHFGa8P4fpCXosHm9PqwbiwzHAqyLvlvPP+dEKWG23dyh6C1g0RY0Jsv+Dm77/XwIAWlpbVzJh7gLAnHjw8d27z5V65xW/AVGM6Ekx9nZCAAAAAElFTkSuQmCC",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "light"
+ },
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABoUlEQVRIibWUPS+EQRSFz0hsxaIgIZHYllDYiiiUEn6Bn0Dho6Nhf4GPjkYn8ZEgGqFRSNBoVTQKdhEROsk+mrt2svvu7Gutk7yZzJlz77nzztyR/hmulADSkkYk5SQdO+c+QwmAZkkTktolXTjnbkLiDJCniHsgFdCnTFNAHliuJE6bYANoAYaBF+AwYHBkmiGgFdi0HINR4lmrotXjVoG3gMEbsOLN2yzHTIFr8PRZG3s9rs/jo5At0fd6fFk1TfY/X4A14MyqmQrsYNo0pxbzCtwBTZUCUsAh8GHCKaDspnl6ZyZ3FnMA9AR2/BOYBzJVhUV9BshHrTVEkZKeJPXHNZA0IOkxttrrhzkgGdAlgXnTLv3GIAHsEh87QGNUrooHaEajkoYlFXYxaeO2je+SLp1z57Grr2J4DvwqWaVDrhv+3SAWrMvXgWcgZ10b3a01GuwDX8CWfV/AXr2Sd9lVXPC4ReM6q8XHOYMOG2897rZkrXZY0+WAK6DHHsRr4xJ/NjCTcXstC/gAxuPEBju5xKRb0phNT5xzD7UUW3d8A4p92DZKdSwEAAAAAElFTkSuQmCC",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "dark"
+ }
+ ]
}
\ No newline at end of file
diff --git a/pkg/github/__toolsnaps__/merge_pull_request.snap b/pkg/github/__toolsnaps__/merge_pull_request.snap
index 50d040f2a..2801b6a53 100644
--- a/pkg/github/__toolsnaps__/merge_pull_request.snap
+++ b/pkg/github/__toolsnaps__/merge_pull_request.snap
@@ -42,5 +42,23 @@
}
}
},
- "name": "merge_pull_request"
+ "name": "merge_pull_request",
+ "icons": [
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACeElEQVRIibWVTUhUYRSGn/e74+iiQih1F9Vcmj9sptylUVBYkO4jcNeuJBdFKxe1CYQokGrRKjCEdtmqwEVmtqomQWeiUdc2EBUtUufe0yLHn1KLGXtX5zvn4zz3vd8f/Gfp90Qs0drmpA6MT1EveDo1NfV92wB+KnMdo39Nfs4L7eSHD5Nz1QJcJYglWtsw+iUehAuRRjO1g+0KHLerbb4OIHnHAC1FdW129s3XmUJuwnBDoOPbA7BwHsD7QWq1HKYN5msBRCpB1AueLoSROSkciSUyj5ClhE6BLtYC8CpBqVRabNrdMmIiJdQjuUbQ1WI+d78WwIbykxnzU9np7ejlNq2YxQ4ebNtTKyCyWcEgYl55EDj/a7ihFEtkLkr0As2YxjwL+9aem00dCEYNzvnJzLDvH27aaM5y80HEnKGHKGwPnEbT6fSOvzpAmrDQnkncpC7siiUzz2QqIPu25iOuGBorTufO/AJmH0v2ajHwuoHhrQHATOH9rQPJ7IjDLgs6kZ0F6it1AzArVcZLdUE+WnYgmv/uYFmz+dxH4NJGNT+RfYLCE7F4tn0pGkxHy94AmBm8/GfAVvIs7AukUTkbj5YdYIbZ9WJh8m1lzrrbNB4/tD+QuyPsdCibF26gmM/dY/NdRDqd3rEYeN04mswYL+ZXm68DxOPxnWXXMClsp+GGhCWBTtClYj53t1qXK78oVH2XYB/mHZ0pvHsN4Cczzw3rBaoGrJ6D5ZUvN1i+kjI0LWiptjmscbC88hZZCAf2trZeq1v0UsJ6wF7UAlhxUMxPvkW6AboQLbvPcjaO+BIx11cL4I9H308eOiLRQUhpOx79/66fNKzrOCYNDm0AAAAASUVORK5CYII=",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "light"
+ },
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABjElEQVRIibWVPS/DURTGnysSC0HiZdWVrZ28JDaLT8BHaBsMdjqZJDXiAzC2LF5mX6GtATGiIsGARH+Gnj9X8a/kf3uWe3Py3Oc559xz75E6bK7VAWQkzUi6lXTonHsOpgYUgAZfdgmkQpFnjHwb6AemgDpQCiWwYlEPeL4i8JCEt8vb39g67vkmPH8yA3qt5nVgCzi1jLJBBEwkBZSAdxPKAj86LYQQQCU4cYvAKzDUSYF3YC+uRIAD8sA58ACU//VuTODE1n1g+A9c3jBH1tJ1a5TeCPNrdACSCpKeJG1IepN0LKkm6dGDrkqqOOdm7dyUpDNJi865PUnqjsvEObcJHEhaljQnaV5STwvszttXbR2J441KtB4LauLKVpZpYBDYte8mHUogZTWPrAGstTtQBl6AayDX7qHZD7AALMVGDvQBV5ZyETi2qHLtMvmXWRQAk57vBKgl4fV/0+jmq56vImk0icCnAWm7pB3riGngnlADx0TW+T4yL4CxJJy/Df20mkP/TqGHfifsA7INs3X5i3+yAAAAAElFTkSuQmCC",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "dark"
+ }
+ ]
}
\ No newline at end of file
diff --git a/pkg/github/__toolsnaps__/request_copilot_review.snap b/pkg/github/__toolsnaps__/request_copilot_review.snap
index b967b51cc..06828776c 100644
--- a/pkg/github/__toolsnaps__/request_copilot_review.snap
+++ b/pkg/github/__toolsnaps__/request_copilot_review.snap
@@ -25,5 +25,23 @@
}
}
},
- "name": "request_copilot_review"
+ "name": "request_copilot_review",
+ "icons": [
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "light"
+ },
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "dark"
+ }
+ ]
}
\ No newline at end of file
diff --git a/pkg/github/__toolsnaps__/star_repository.snap b/pkg/github/__toolsnaps__/star_repository.snap
index 382d40395..165a011bd 100644
--- a/pkg/github/__toolsnaps__/star_repository.snap
+++ b/pkg/github/__toolsnaps__/star_repository.snap
@@ -20,5 +20,23 @@
}
}
},
- "name": "star_repository"
+ "name": "star_repository",
+ "icons": [
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACG0lEQVRIidWVMWgTYRiGn+/+a21EClGrERRiTWLShrbiUETErDq7u3QRF0WhoKN06uYgKEVx1lGQLjo4OTUJ2FzSpBrEQiCkGYPm7nMwlZBe2rvaxXf6eb//fd//u/+7O0MIJDJz905MnJpvNRufg2oksHli5iwjUgXExUp9La3Vg+isoAGMyiJwBBi11XsQVBaog0zm8plfdGtApEd1LJdEpVL4sZ82UAc/cRf7zAHGPKMPg2j37eB8NnvauGYTODpQ6hjPulAur23tpTd7FePx+JhtIkvAVZ+yraJj48ciH9rtdneYhwCk03NxV5hWNAWSVLykIEngHPs/Rg/4ruiGYG2AbghSMcoXx8l/k3R6Lt4V3STEyAaE2iqTluPk66Arh2wO6Irj5OsGoNVsvIuejEVFmD8Ua+V5zSneAfTvJW83G6vHJ2LjwJV/tH9Wc4p3AYWBKWo1G6vRiZgRuH4ga3S5Vire7+d2jel2s/HxICEKT2ql4qNB3ncEbU9fhTEHGFF56cf7BrhCNmyAi/pqhr1EoQN0iGZIgEyHDUDw1dghNneB1731bR9tsA5yuZwNZPooBd4YT7PVUmGhWios2CpJEV7w5zu0g0xPO3DWAUymZ1OWUO6V3yP6uLpeWPM7XWJq9hIqS6A3ADzl4qZTqPTv2ZUYMd2tjms/NZa+rawXPvkZ76AXfDM1NXPN9eRWxHT3/Df8n/gNrfGxihYBZk0AAAAASUVORK5CYII=",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "light"
+ },
+ {
+ "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABLElEQVRIidWVTUrDQBiG3ylddNG9+IdRMC7qpgfoWTyCnqFLF/62UMGNWw/gTdwoiMUzaCtoHxeZ4BQn6SQdEV8IZPG9zzMzCYlUIcARcFilUwW+AUyBd2DrNwSXfOciNnwVeHMEE2A9puCMnzmNBV8BXj2CCbC2LLwFDDzwPAOgVcYwFpRI6khKJe0616akxoJ1zCS9SHp0rgdJ98aYZ2PhT7ksYpC005A0lnQdGS7LHGcqMMB5yVlXzQiYP1orOYkAHwLFxw30l4AfBx1eTUk/+OkA2zUEiY9V9I7vB69mQefPBJ0aAm+nWWH4Q9KNvT/wdMN2DTTJ/lx5ZsAtsOfMJMAV8OnMTYGiBc8JUqd0B3RLZrt2Jk8aImiTfTZ6QVvOOj3baYd2/k++AC+3Yx0GcXS0AAAAAElFTkSuQmCC",
+ "mimeType": "image/png",
+ "sizes": [
+ "24x24"
+ ],
+ "theme": "dark"
+ }
+ ]
}
\ No newline at end of file
diff --git a/pkg/github/issues.go b/pkg/github/issues.go
index 806dc5701..65a2a455d 100644
--- a/pkg/github/issues.go
+++ b/pkg/github/issues.go
@@ -12,6 +12,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/inventory"
"github.com/github/github-mcp-server/pkg/lockdown"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/github/github-mcp-server/pkg/sanitize"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
@@ -1622,6 +1623,7 @@ func AssignCopilotToIssue(t translations.TranslationHelperFunc) inventory.Server
mcp.Tool{
Name: "assign_copilot_to_issue",
Description: t("TOOL_ASSIGN_COPILOT_TO_ISSUE_DESCRIPTION", description.String()),
+ Icons: octicons.Icons("copilot"),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_ASSIGN_COPILOT_TO_ISSUE_USER_TITLE", "Assign Copilot to issue"),
ReadOnlyHint: false,
diff --git a/pkg/github/pullrequests.go b/pkg/github/pullrequests.go
index 78b841356..b8784e4c2 100644
--- a/pkg/github/pullrequests.go
+++ b/pkg/github/pullrequests.go
@@ -16,6 +16,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/inventory"
"github.com/github/github-mcp-server/pkg/lockdown"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/github/github-mcp-server/pkg/sanitize"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
@@ -1086,6 +1087,7 @@ func MergePullRequest(t translations.TranslationHelperFunc) inventory.ServerTool
mcp.Tool{
Name: "merge_pull_request",
Description: t("TOOL_MERGE_PULL_REQUEST_DESCRIPTION", "Merge a pull request in a GitHub repository."),
+ Icons: octicons.Icons("git-merge"),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_MERGE_PULL_REQUEST_USER_TITLE", "Merge pull request"),
ReadOnlyHint: false,
@@ -1855,6 +1857,7 @@ func RequestCopilotReview(t translations.TranslationHelperFunc) inventory.Server
mcp.Tool{
Name: "request_copilot_review",
Description: t("TOOL_REQUEST_COPILOT_REVIEW_DESCRIPTION", "Request a GitHub Copilot code review for a pull request. Use this for automated feedback on pull requests, usually before requesting a human reviewer."),
+ Icons: octicons.Icons("copilot"),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_REQUEST_COPILOT_REVIEW_USER_TITLE", "Request Copilot review"),
ReadOnlyHint: false,
diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go
index 179c08475..6d6c949a7 100644
--- a/pkg/github/repositories.go
+++ b/pkg/github/repositories.go
@@ -11,6 +11,7 @@ import (
ghErrors "github.com/github/github-mcp-server/pkg/errors"
"github.com/github/github-mcp-server/pkg/inventory"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/github/github-mcp-server/pkg/utils"
@@ -810,6 +811,7 @@ func ForkRepository(t translations.TranslationHelperFunc) inventory.ServerTool {
mcp.Tool{
Name: "fork_repository",
Description: t("TOOL_FORK_REPOSITORY_DESCRIPTION", "Fork a GitHub repository to your account or specified organization"),
+ Icons: octicons.Icons("repo-forked"),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_FORK_REPOSITORY_USER_TITLE", "Fork repository"),
ReadOnlyHint: false,
@@ -2096,6 +2098,7 @@ func StarRepository(t translations.TranslationHelperFunc) inventory.ServerTool {
mcp.Tool{
Name: "star_repository",
Description: t("TOOL_STAR_REPOSITORY_DESCRIPTION", "Star a GitHub repository"),
+ Icons: octicons.Icons("star-fill"),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_STAR_REPOSITORY_USER_TITLE", "Star repository"),
ReadOnlyHint: false,
diff --git a/pkg/github/repository_resource.go b/pkg/github/repository_resource.go
index 9a937a8ca..ee43e9d04 100644
--- a/pkg/github/repository_resource.go
+++ b/pkg/github/repository_resource.go
@@ -14,6 +14,7 @@ import (
"strings"
"github.com/github/github-mcp-server/pkg/inventory"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/github/github-mcp-server/pkg/raw"
"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v79/github"
@@ -37,6 +38,7 @@ func GetRepositoryResourceContent(t translations.TranslationHelperFunc) inventor
Name: "repository_content",
URITemplate: repositoryResourceContentURITemplate.Raw(),
Description: t("RESOURCE_REPOSITORY_CONTENT_DESCRIPTION", "Repository Content"),
+ Icons: octicons.Icons("repo"),
},
repositoryResourceContentsHandlerFunc(repositoryResourceContentURITemplate),
)
@@ -50,6 +52,7 @@ func GetRepositoryResourceBranchContent(t translations.TranslationHelperFunc) in
Name: "repository_content_branch",
URITemplate: repositoryResourceBranchContentURITemplate.Raw(),
Description: t("RESOURCE_REPOSITORY_CONTENT_BRANCH_DESCRIPTION", "Repository Content for specific branch"),
+ Icons: octicons.Icons("git-branch"),
},
repositoryResourceContentsHandlerFunc(repositoryResourceBranchContentURITemplate),
)
@@ -63,6 +66,7 @@ func GetRepositoryResourceCommitContent(t translations.TranslationHelperFunc) in
Name: "repository_content_commit",
URITemplate: repositoryResourceCommitContentURITemplate.Raw(),
Description: t("RESOURCE_REPOSITORY_CONTENT_COMMIT_DESCRIPTION", "Repository Content for specific commit"),
+ Icons: octicons.Icons("git-commit"),
},
repositoryResourceContentsHandlerFunc(repositoryResourceCommitContentURITemplate),
)
@@ -76,6 +80,7 @@ func GetRepositoryResourceTagContent(t translations.TranslationHelperFunc) inven
Name: "repository_content_tag",
URITemplate: repositoryResourceTagContentURITemplate.Raw(),
Description: t("RESOURCE_REPOSITORY_CONTENT_TAG_DESCRIPTION", "Repository Content for specific tag"),
+ Icons: octicons.Icons("tag"),
},
repositoryResourceContentsHandlerFunc(repositoryResourceTagContentURITemplate),
)
@@ -89,6 +94,7 @@ func GetRepositoryResourcePrContent(t translations.TranslationHelperFunc) invent
Name: "repository_content_pr",
URITemplate: repositoryResourcePrContentURITemplate.Raw(),
Description: t("RESOURCE_REPOSITORY_CONTENT_PR_DESCRIPTION", "Repository Content for specific pull request"),
+ Icons: octicons.Icons("git-pull-request"),
},
repositoryResourceContentsHandlerFunc(repositoryResourcePrContentURITemplate),
)
diff --git a/pkg/github/server.go b/pkg/github/server.go
index a9c9305a2..8248da58f 100644
--- a/pkg/github/server.go
+++ b/pkg/github/server.go
@@ -8,6 +8,7 @@ import (
"strconv"
"strings"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/github/github-mcp-server/pkg/utils"
"github.com/google/go-github/v79/github"
"github.com/google/jsonschema-go/jsonschema"
@@ -26,6 +27,7 @@ func NewServer(version string, opts *mcp.ServerOptions) *mcp.Server {
Name: "github-mcp-server",
Title: "GitHub MCP Server",
Version: version,
+ Icons: octicons.Icons("mark-github"),
}, opts)
return s
diff --git a/pkg/github/tools.go b/pkg/github/tools.go
index 62d2d8664..10877adf2 100644
--- a/pkg/github/tools.go
+++ b/pkg/github/tools.go
@@ -15,99 +15,140 @@ type GetGQLClientFn func(context.Context) (*githubv4.Client, error)
// Toolset metadata constants - these define all available toolsets and their descriptions.
// Tools use these constants to declare which toolset they belong to.
+// Icons are Octicon names from https://primer.style/foundations/icons
var (
ToolsetMetadataAll = inventory.ToolsetMetadata{
ID: "all",
Description: "Special toolset that enables all available toolsets",
+ Icon: "apps",
}
ToolsetMetadataDefault = inventory.ToolsetMetadata{
ID: "default",
Description: "Special toolset that enables the default toolset configuration. When no toolsets are specified, this is the set that is enabled",
+ Icon: "check-circle",
}
ToolsetMetadataContext = inventory.ToolsetMetadata{
ID: "context",
Description: "Tools that provide context about the current user and GitHub context you are operating in",
Default: true,
+ Icon: "person",
}
ToolsetMetadataRepos = inventory.ToolsetMetadata{
ID: "repos",
Description: "GitHub Repository related tools",
Default: true,
+ Icon: "repo",
}
ToolsetMetadataGit = inventory.ToolsetMetadata{
ID: "git",
Description: "GitHub Git API related tools for low-level Git operations",
+ Icon: "git-branch",
}
ToolsetMetadataIssues = inventory.ToolsetMetadata{
ID: "issues",
Description: "GitHub Issues related tools",
Default: true,
+ Icon: "issue-opened",
}
ToolsetMetadataPullRequests = inventory.ToolsetMetadata{
ID: "pull_requests",
Description: "GitHub Pull Request related tools",
Default: true,
+ Icon: "git-pull-request",
}
ToolsetMetadataUsers = inventory.ToolsetMetadata{
ID: "users",
Description: "GitHub User related tools",
Default: true,
+ Icon: "people",
}
ToolsetMetadataOrgs = inventory.ToolsetMetadata{
ID: "orgs",
Description: "GitHub Organization related tools",
+ Icon: "organization",
}
ToolsetMetadataActions = inventory.ToolsetMetadata{
ID: "actions",
Description: "GitHub Actions workflows and CI/CD operations",
+ Icon: "workflow",
}
ToolsetMetadataCodeSecurity = inventory.ToolsetMetadata{
ID: "code_security",
Description: "Code security related tools, such as GitHub Code Scanning",
+ Icon: "codescan",
}
ToolsetMetadataSecretProtection = inventory.ToolsetMetadata{
ID: "secret_protection",
Description: "Secret protection related tools, such as GitHub Secret Scanning",
+ Icon: "shield-lock",
}
ToolsetMetadataDependabot = inventory.ToolsetMetadata{
ID: "dependabot",
Description: "Dependabot tools",
+ Icon: "dependabot",
}
ToolsetMetadataNotifications = inventory.ToolsetMetadata{
ID: "notifications",
Description: "GitHub Notifications related tools",
+ Icon: "bell",
}
ToolsetMetadataExperiments = inventory.ToolsetMetadata{
ID: "experiments",
Description: "Experimental features that are not considered stable yet",
+ Icon: "beaker",
}
ToolsetMetadataDiscussions = inventory.ToolsetMetadata{
ID: "discussions",
Description: "GitHub Discussions related tools",
+ Icon: "comment-discussion",
}
ToolsetMetadataGists = inventory.ToolsetMetadata{
ID: "gists",
Description: "GitHub Gist related tools",
+ Icon: "logo-gist",
}
ToolsetMetadataSecurityAdvisories = inventory.ToolsetMetadata{
ID: "security_advisories",
Description: "Security advisories related tools",
+ Icon: "shield",
}
ToolsetMetadataProjects = inventory.ToolsetMetadata{
ID: "projects",
Description: "GitHub Projects related tools",
+ Icon: "project",
}
ToolsetMetadataStargazers = inventory.ToolsetMetadata{
ID: "stargazers",
Description: "GitHub Stargazers related tools",
+ Icon: "star",
}
ToolsetMetadataDynamic = inventory.ToolsetMetadata{
ID: "dynamic",
Description: "Discover GitHub MCP tools that can help achieve tasks by enabling additional sets of tools, you can control the enablement of any toolset to access its tools when this toolset is enabled.",
+ Icon: "tools",
}
ToolsetLabels = inventory.ToolsetMetadata{
ID: "labels",
Description: "GitHub Labels related tools",
+ Icon: "tag",
+ }
+
+ // Remote-only toolsets - these are only available in the remote MCP server
+ // but are documented here for consistency and to enable automated documentation.
+ ToolsetMetadataCopilot = inventory.ToolsetMetadata{
+ ID: "copilot",
+ Description: "Copilot related tools",
+ Icon: "copilot",
+ }
+ ToolsetMetadataCopilotSpaces = inventory.ToolsetMetadata{
+ ID: "copilot_spaces",
+ Description: "Copilot Spaces tools",
+ Icon: "copilot",
+ }
+ ToolsetMetadataSupportSearch = inventory.ToolsetMetadata{
+ ID: "github_support_docs_search",
+ Description: "Retrieve documentation to answer GitHub product and support questions. Topics include: GitHub Actions Workflows, Authentication, ...",
+ Icon: "book",
}
)
@@ -405,3 +446,14 @@ func GetDefaultToolsetIDs() []string {
}
return result
}
+
+// RemoteOnlyToolsets returns toolset metadata for toolsets that are only
+// available in the remote MCP server. These are documented but not registered
+// in the local server.
+func RemoteOnlyToolsets() []inventory.ToolsetMetadata {
+ return []inventory.ToolsetMetadata{
+ ToolsetMetadataCopilot,
+ ToolsetMetadataCopilotSpaces,
+ ToolsetMetadataSupportSearch,
+ }
+}
diff --git a/pkg/github/toolset_icons_test.go b/pkg/github/toolset_icons_test.go
new file mode 100644
index 000000000..fd9cec462
--- /dev/null
+++ b/pkg/github/toolset_icons_test.go
@@ -0,0 +1,86 @@
+package github
+
+import (
+ "testing"
+
+ "github.com/github/github-mcp-server/pkg/octicons"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestAllToolsetIconsExist validates that every toolset with an Icon field
+// references an icon that actually exists in the embedded octicons.
+// This prevents broken icon references from being merged.
+func TestAllToolsetIconsExist(t *testing.T) {
+ // Get all available toolsets from the inventory
+ inv := NewInventory(stubTranslator).Build()
+ toolsets := inv.AvailableToolsets()
+
+ // Also test remote-only toolsets
+ remoteToolsets := RemoteOnlyToolsets()
+
+ // Combine both lists
+ allToolsets := make([]struct {
+ name string
+ icon string
+ }, 0)
+
+ for _, ts := range toolsets {
+ if ts.Icon != "" {
+ allToolsets = append(allToolsets, struct {
+ name string
+ icon string
+ }{name: string(ts.ID), icon: ts.Icon})
+ }
+ }
+
+ for _, ts := range remoteToolsets {
+ if ts.Icon != "" {
+ allToolsets = append(allToolsets, struct {
+ name string
+ icon string
+ }{name: string(ts.ID), icon: ts.Icon})
+ }
+ }
+
+ require.NotEmpty(t, allToolsets, "expected at least one toolset with an icon")
+
+ for _, ts := range allToolsets {
+ t.Run(ts.name, func(t *testing.T) {
+ // Check that icons return valid data URIs (not empty)
+ icons := octicons.Icons(ts.icon)
+ require.NotNil(t, icons, "toolset %s references icon %q which does not exist", ts.name, ts.icon)
+ assert.Len(t, icons, 2, "expected light and dark icon variants for toolset %s", ts.name)
+
+ // Verify both variants have valid data URIs
+ for _, icon := range icons {
+ assert.NotEmpty(t, icon.Source, "icon source should not be empty for toolset %s", ts.name)
+ assert.Contains(t, icon.Source, "data:image/png;base64,",
+ "icon %s for toolset %s should be a valid data URI", ts.icon, ts.name)
+ }
+ })
+ }
+}
+
+// TestToolsetMetadataHasIcons ensures all toolsets have icons defined.
+// This is a policy test - if you want to allow toolsets without icons,
+// you can remove or modify this test.
+func TestToolsetMetadataHasIcons(t *testing.T) {
+ // These toolsets are expected to NOT have icons (internal/special purpose)
+ exceptionsWithoutIcons := map[string]bool{
+ "all": true, // Meta-toolset
+ "default": true, // Meta-toolset
+ }
+
+ inv := NewInventory(stubTranslator).Build()
+ toolsets := inv.AvailableToolsets()
+
+ for _, ts := range toolsets {
+ if exceptionsWithoutIcons[string(ts.ID)] {
+ continue
+ }
+ t.Run(string(ts.ID), func(t *testing.T) {
+ assert.NotEmpty(t, ts.Icon, "toolset %s should have an icon defined", ts.ID)
+ })
+ }
+}
diff --git a/pkg/inventory/registry.go b/pkg/inventory/registry.go
index 204a92d9c..f3691e38a 100644
--- a/pkg/inventory/registry.go
+++ b/pkg/inventory/registry.go
@@ -178,17 +178,31 @@ func (r *Inventory) RegisterTools(ctx context.Context, s *mcp.Server, deps any)
// RegisterResourceTemplates registers all available resource templates with the server.
// The context is used for feature flag evaluation.
+// Icons are automatically applied from the toolset metadata if not already set.
func (r *Inventory) RegisterResourceTemplates(ctx context.Context, s *mcp.Server, deps any) {
for _, res := range r.AvailableResourceTemplates(ctx) {
- s.AddResourceTemplate(&res.Template, res.Handler(deps))
+ // Make a shallow copy to avoid mutating the original
+ templateCopy := res.Template
+ // Apply icons from toolset metadata if not already set
+ if len(templateCopy.Icons) == 0 {
+ templateCopy.Icons = res.Toolset.Icons()
+ }
+ s.AddResourceTemplate(&templateCopy, res.Handler(deps))
}
}
// RegisterPrompts registers all available prompts with the server.
// The context is used for feature flag evaluation.
+// Icons are automatically applied from the toolset metadata if not already set.
func (r *Inventory) RegisterPrompts(ctx context.Context, s *mcp.Server) {
for _, prompt := range r.AvailablePrompts(ctx) {
- s.AddPrompt(&prompt.Prompt, prompt.Handler)
+ // Make a shallow copy to avoid mutating the original
+ promptCopy := prompt.Prompt
+ // Apply icons from toolset metadata if not already set
+ if len(promptCopy.Icons) == 0 {
+ promptCopy.Icons = prompt.Toolset.Icons()
+ }
+ s.AddPrompt(&promptCopy, prompt.Handler)
}
}
diff --git a/pkg/inventory/server_tool.go b/pkg/inventory/server_tool.go
index 33ca7aa77..b21df2520 100644
--- a/pkg/inventory/server_tool.go
+++ b/pkg/inventory/server_tool.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
+ "github.com/github/github-mcp-server/pkg/octicons"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
@@ -26,6 +27,16 @@ type ToolsetMetadata struct {
Description string
// Default indicates this toolset should be enabled by default
Default bool
+ // Icon is the name of the Octicon to use for tools in this toolset.
+ // Use the base name without size suffix, e.g., "repo" not "repo-16".
+ // See https://primer.style/foundations/icons for available icons.
+ Icon string
+}
+
+// Icons returns MCP Icon objects for this toolset, or nil if no icon is set.
+// Icons are provided in both 16x16 and 24x24 sizes.
+func (tm ToolsetMetadata) Icons() []mcp.Icon {
+ return octicons.Icons(tm.Icon)
}
// ServerTool represents an MCP tool with metadata and a handler generator function.
@@ -81,10 +92,18 @@ func (st *ServerTool) Handler(deps any) mcp.ToolHandler {
}
// RegisterFunc registers the tool with the server using the provided dependencies.
+// Icons are automatically applied from the toolset metadata if not already set.
+// A shallow copy of the tool is made to avoid mutating the original ServerTool.
// Panics if the tool has no handler - all tools should have handlers.
func (st *ServerTool) RegisterFunc(s *mcp.Server, deps any) {
handler := st.Handler(deps) // This will panic if HandlerFunc is nil
- s.AddTool(&st.Tool, handler)
+ // Make a shallow copy of the tool to avoid mutating the original
+ toolCopy := st.Tool
+ // Apply icons from toolset metadata if tool doesn't have icons set
+ if len(toolCopy.Icons) == 0 {
+ toolCopy.Icons = st.Toolset.Icons()
+ }
+ s.AddTool(&toolCopy, handler)
}
// NewServerTool creates a ServerTool from a tool definition, toolset metadata, and a typed handler function.
diff --git a/pkg/octicons/icons/apps-dark.png b/pkg/octicons/icons/apps-dark.png
new file mode 100644
index 000000000..607468c85
Binary files /dev/null and b/pkg/octicons/icons/apps-dark.png differ
diff --git a/pkg/octicons/icons/apps-light.png b/pkg/octicons/icons/apps-light.png
new file mode 100644
index 000000000..c6328612b
Binary files /dev/null and b/pkg/octicons/icons/apps-light.png differ
diff --git a/pkg/octicons/icons/beaker-dark.png b/pkg/octicons/icons/beaker-dark.png
new file mode 100644
index 000000000..b30a95922
Binary files /dev/null and b/pkg/octicons/icons/beaker-dark.png differ
diff --git a/pkg/octicons/icons/beaker-light.png b/pkg/octicons/icons/beaker-light.png
new file mode 100644
index 000000000..576d170bd
Binary files /dev/null and b/pkg/octicons/icons/beaker-light.png differ
diff --git a/pkg/octicons/icons/bell-dark.png b/pkg/octicons/icons/bell-dark.png
new file mode 100644
index 000000000..a462a8d4c
Binary files /dev/null and b/pkg/octicons/icons/bell-dark.png differ
diff --git a/pkg/octicons/icons/bell-light.png b/pkg/octicons/icons/bell-light.png
new file mode 100644
index 000000000..778215804
Binary files /dev/null and b/pkg/octicons/icons/bell-light.png differ
diff --git a/pkg/octicons/icons/book-dark.png b/pkg/octicons/icons/book-dark.png
new file mode 100644
index 000000000..9658b4f8e
Binary files /dev/null and b/pkg/octicons/icons/book-dark.png differ
diff --git a/pkg/octicons/icons/book-light.png b/pkg/octicons/icons/book-light.png
new file mode 100644
index 000000000..8be91a434
Binary files /dev/null and b/pkg/octicons/icons/book-light.png differ
diff --git a/pkg/octicons/icons/check-circle-dark.png b/pkg/octicons/icons/check-circle-dark.png
new file mode 100644
index 000000000..1fad22056
Binary files /dev/null and b/pkg/octicons/icons/check-circle-dark.png differ
diff --git a/pkg/octicons/icons/check-circle-light.png b/pkg/octicons/icons/check-circle-light.png
new file mode 100644
index 000000000..75916ed24
Binary files /dev/null and b/pkg/octicons/icons/check-circle-light.png differ
diff --git a/pkg/octicons/icons/codescan-dark.png b/pkg/octicons/icons/codescan-dark.png
new file mode 100644
index 000000000..7bedddfe4
Binary files /dev/null and b/pkg/octicons/icons/codescan-dark.png differ
diff --git a/pkg/octicons/icons/codescan-light.png b/pkg/octicons/icons/codescan-light.png
new file mode 100644
index 000000000..cb3fb7545
Binary files /dev/null and b/pkg/octicons/icons/codescan-light.png differ
diff --git a/pkg/octicons/icons/comment-discussion-dark.png b/pkg/octicons/icons/comment-discussion-dark.png
new file mode 100644
index 000000000..6b7eeb6ef
Binary files /dev/null and b/pkg/octicons/icons/comment-discussion-dark.png differ
diff --git a/pkg/octicons/icons/comment-discussion-light.png b/pkg/octicons/icons/comment-discussion-light.png
new file mode 100644
index 000000000..64ee5f0ca
Binary files /dev/null and b/pkg/octicons/icons/comment-discussion-light.png differ
diff --git a/pkg/octicons/icons/copilot-dark.png b/pkg/octicons/icons/copilot-dark.png
new file mode 100644
index 000000000..2188a1bca
Binary files /dev/null and b/pkg/octicons/icons/copilot-dark.png differ
diff --git a/pkg/octicons/icons/copilot-light.png b/pkg/octicons/icons/copilot-light.png
new file mode 100644
index 000000000..4e83af015
Binary files /dev/null and b/pkg/octicons/icons/copilot-light.png differ
diff --git a/pkg/octicons/icons/dependabot-dark.png b/pkg/octicons/icons/dependabot-dark.png
new file mode 100644
index 000000000..39d41c7d1
Binary files /dev/null and b/pkg/octicons/icons/dependabot-dark.png differ
diff --git a/pkg/octicons/icons/dependabot-light.png b/pkg/octicons/icons/dependabot-light.png
new file mode 100644
index 000000000..5dfa5b920
Binary files /dev/null and b/pkg/octicons/icons/dependabot-light.png differ
diff --git a/pkg/octicons/icons/file-dark.png b/pkg/octicons/icons/file-dark.png
new file mode 100644
index 000000000..213069bc9
Binary files /dev/null and b/pkg/octicons/icons/file-dark.png differ
diff --git a/pkg/octicons/icons/file-light.png b/pkg/octicons/icons/file-light.png
new file mode 100644
index 000000000..8a00ffc25
Binary files /dev/null and b/pkg/octicons/icons/file-light.png differ
diff --git a/pkg/octicons/icons/git-branch-dark.png b/pkg/octicons/icons/git-branch-dark.png
new file mode 100644
index 000000000..3c4756dfd
Binary files /dev/null and b/pkg/octicons/icons/git-branch-dark.png differ
diff --git a/pkg/octicons/icons/git-branch-light.png b/pkg/octicons/icons/git-branch-light.png
new file mode 100644
index 000000000..42eb954de
Binary files /dev/null and b/pkg/octicons/icons/git-branch-light.png differ
diff --git a/pkg/octicons/icons/git-commit-dark.png b/pkg/octicons/icons/git-commit-dark.png
new file mode 100644
index 000000000..69f72a47b
Binary files /dev/null and b/pkg/octicons/icons/git-commit-dark.png differ
diff --git a/pkg/octicons/icons/git-commit-light.png b/pkg/octicons/icons/git-commit-light.png
new file mode 100644
index 000000000..8011fbcb6
Binary files /dev/null and b/pkg/octicons/icons/git-commit-light.png differ
diff --git a/pkg/octicons/icons/git-merge-dark.png b/pkg/octicons/icons/git-merge-dark.png
new file mode 100644
index 000000000..283ee665c
Binary files /dev/null and b/pkg/octicons/icons/git-merge-dark.png differ
diff --git a/pkg/octicons/icons/git-merge-light.png b/pkg/octicons/icons/git-merge-light.png
new file mode 100644
index 000000000..e208a09f5
Binary files /dev/null and b/pkg/octicons/icons/git-merge-light.png differ
diff --git a/pkg/octicons/icons/git-pull-request-dark.png b/pkg/octicons/icons/git-pull-request-dark.png
new file mode 100644
index 000000000..bdbc8bd27
Binary files /dev/null and b/pkg/octicons/icons/git-pull-request-dark.png differ
diff --git a/pkg/octicons/icons/git-pull-request-light.png b/pkg/octicons/icons/git-pull-request-light.png
new file mode 100644
index 000000000..616ece21a
Binary files /dev/null and b/pkg/octicons/icons/git-pull-request-light.png differ
diff --git a/pkg/octicons/icons/issue-opened-dark.png b/pkg/octicons/icons/issue-opened-dark.png
new file mode 100644
index 000000000..d71f5ec2c
Binary files /dev/null and b/pkg/octicons/icons/issue-opened-dark.png differ
diff --git a/pkg/octicons/icons/issue-opened-light.png b/pkg/octicons/icons/issue-opened-light.png
new file mode 100644
index 000000000..123212013
Binary files /dev/null and b/pkg/octicons/icons/issue-opened-light.png differ
diff --git a/pkg/octicons/icons/logo-gist-dark.png b/pkg/octicons/icons/logo-gist-dark.png
new file mode 100644
index 000000000..8929edee4
Binary files /dev/null and b/pkg/octicons/icons/logo-gist-dark.png differ
diff --git a/pkg/octicons/icons/logo-gist-light.png b/pkg/octicons/icons/logo-gist-light.png
new file mode 100644
index 000000000..364ef951a
Binary files /dev/null and b/pkg/octicons/icons/logo-gist-light.png differ
diff --git a/pkg/octicons/icons/mark-github-dark.png b/pkg/octicons/icons/mark-github-dark.png
new file mode 100644
index 000000000..57f11abfd
Binary files /dev/null and b/pkg/octicons/icons/mark-github-dark.png differ
diff --git a/pkg/octicons/icons/mark-github-light.png b/pkg/octicons/icons/mark-github-light.png
new file mode 100644
index 000000000..7d7ffd123
Binary files /dev/null and b/pkg/octicons/icons/mark-github-light.png differ
diff --git a/pkg/octicons/icons/organization-dark.png b/pkg/octicons/icons/organization-dark.png
new file mode 100644
index 000000000..6ad3feaf8
Binary files /dev/null and b/pkg/octicons/icons/organization-dark.png differ
diff --git a/pkg/octicons/icons/organization-light.png b/pkg/octicons/icons/organization-light.png
new file mode 100644
index 000000000..e504febe3
Binary files /dev/null and b/pkg/octicons/icons/organization-light.png differ
diff --git a/pkg/octicons/icons/people-dark.png b/pkg/octicons/icons/people-dark.png
new file mode 100644
index 000000000..2dd60bab6
Binary files /dev/null and b/pkg/octicons/icons/people-dark.png differ
diff --git a/pkg/octicons/icons/people-light.png b/pkg/octicons/icons/people-light.png
new file mode 100644
index 000000000..5dc0fb62f
Binary files /dev/null and b/pkg/octicons/icons/people-light.png differ
diff --git a/pkg/octicons/icons/person-dark.png b/pkg/octicons/icons/person-dark.png
new file mode 100644
index 000000000..c0fdf6cad
Binary files /dev/null and b/pkg/octicons/icons/person-dark.png differ
diff --git a/pkg/octicons/icons/person-light.png b/pkg/octicons/icons/person-light.png
new file mode 100644
index 000000000..db1368350
Binary files /dev/null and b/pkg/octicons/icons/person-light.png differ
diff --git a/pkg/octicons/icons/project-dark.png b/pkg/octicons/icons/project-dark.png
new file mode 100644
index 000000000..273d7ba5a
Binary files /dev/null and b/pkg/octicons/icons/project-dark.png differ
diff --git a/pkg/octicons/icons/project-light.png b/pkg/octicons/icons/project-light.png
new file mode 100644
index 000000000..51e232d29
Binary files /dev/null and b/pkg/octicons/icons/project-light.png differ
diff --git a/pkg/octicons/icons/repo-dark.png b/pkg/octicons/icons/repo-dark.png
new file mode 100644
index 000000000..81bbeac25
Binary files /dev/null and b/pkg/octicons/icons/repo-dark.png differ
diff --git a/pkg/octicons/icons/repo-forked-dark.png b/pkg/octicons/icons/repo-forked-dark.png
new file mode 100644
index 000000000..434d5d287
Binary files /dev/null and b/pkg/octicons/icons/repo-forked-dark.png differ
diff --git a/pkg/octicons/icons/repo-forked-light.png b/pkg/octicons/icons/repo-forked-light.png
new file mode 100644
index 000000000..bf41f6e27
Binary files /dev/null and b/pkg/octicons/icons/repo-forked-light.png differ
diff --git a/pkg/octicons/icons/repo-light.png b/pkg/octicons/icons/repo-light.png
new file mode 100644
index 000000000..185a05438
Binary files /dev/null and b/pkg/octicons/icons/repo-light.png differ
diff --git a/pkg/octicons/icons/shield-dark.png b/pkg/octicons/icons/shield-dark.png
new file mode 100644
index 000000000..cf61060de
Binary files /dev/null and b/pkg/octicons/icons/shield-dark.png differ
diff --git a/pkg/octicons/icons/shield-light.png b/pkg/octicons/icons/shield-light.png
new file mode 100644
index 000000000..5a11004ee
Binary files /dev/null and b/pkg/octicons/icons/shield-light.png differ
diff --git a/pkg/octicons/icons/shield-lock-dark.png b/pkg/octicons/icons/shield-lock-dark.png
new file mode 100644
index 000000000..0abf4ad3f
Binary files /dev/null and b/pkg/octicons/icons/shield-lock-dark.png differ
diff --git a/pkg/octicons/icons/shield-lock-light.png b/pkg/octicons/icons/shield-lock-light.png
new file mode 100644
index 000000000..ae6e8cc1b
Binary files /dev/null and b/pkg/octicons/icons/shield-lock-light.png differ
diff --git a/pkg/octicons/icons/star-dark.png b/pkg/octicons/icons/star-dark.png
new file mode 100644
index 000000000..9156c9b28
Binary files /dev/null and b/pkg/octicons/icons/star-dark.png differ
diff --git a/pkg/octicons/icons/star-fill-dark.png b/pkg/octicons/icons/star-fill-dark.png
new file mode 100644
index 000000000..3b19107d7
Binary files /dev/null and b/pkg/octicons/icons/star-fill-dark.png differ
diff --git a/pkg/octicons/icons/star-fill-light.png b/pkg/octicons/icons/star-fill-light.png
new file mode 100644
index 000000000..fd3621016
Binary files /dev/null and b/pkg/octicons/icons/star-fill-light.png differ
diff --git a/pkg/octicons/icons/star-light.png b/pkg/octicons/icons/star-light.png
new file mode 100644
index 000000000..54372238e
Binary files /dev/null and b/pkg/octicons/icons/star-light.png differ
diff --git a/pkg/octicons/icons/tag-dark.png b/pkg/octicons/icons/tag-dark.png
new file mode 100644
index 000000000..00d0a56ff
Binary files /dev/null and b/pkg/octicons/icons/tag-dark.png differ
diff --git a/pkg/octicons/icons/tag-light.png b/pkg/octicons/icons/tag-light.png
new file mode 100644
index 000000000..cead91700
Binary files /dev/null and b/pkg/octicons/icons/tag-light.png differ
diff --git a/pkg/octicons/icons/tools-dark.png b/pkg/octicons/icons/tools-dark.png
new file mode 100644
index 000000000..2cf9080c7
Binary files /dev/null and b/pkg/octicons/icons/tools-dark.png differ
diff --git a/pkg/octicons/icons/tools-light.png b/pkg/octicons/icons/tools-light.png
new file mode 100644
index 000000000..59cf00d11
Binary files /dev/null and b/pkg/octicons/icons/tools-light.png differ
diff --git a/pkg/octicons/icons/workflow-dark.png b/pkg/octicons/icons/workflow-dark.png
new file mode 100644
index 000000000..f1339416f
Binary files /dev/null and b/pkg/octicons/icons/workflow-dark.png differ
diff --git a/pkg/octicons/icons/workflow-light.png b/pkg/octicons/icons/workflow-light.png
new file mode 100644
index 000000000..6930f846d
Binary files /dev/null and b/pkg/octicons/icons/workflow-light.png differ
diff --git a/pkg/octicons/octicons.go b/pkg/octicons/octicons.go
new file mode 100644
index 000000000..c6b92c47b
--- /dev/null
+++ b/pkg/octicons/octicons.go
@@ -0,0 +1,83 @@
+// Package octicons provides helpers for working with GitHub Octicon icons.
+// See https://primer.style/foundations/icons for available icons.
+package octicons
+
+import (
+ "bufio"
+ "embed"
+ "encoding/base64"
+ "fmt"
+ "strings"
+
+ "github.com/modelcontextprotocol/go-sdk/mcp"
+)
+
+//go:embed icons/*.png
+var iconsFS embed.FS
+
+//go:embed required_icons.txt
+var requiredIconsTxt string
+
+// RequiredIcons returns the list of icon names from required_icons.txt.
+// This is the single source of truth for which icons should be embedded.
+func RequiredIcons() []string {
+ var icons []string
+ scanner := bufio.NewScanner(strings.NewReader(requiredIconsTxt))
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ // Skip empty lines and comments
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ icons = append(icons, line)
+ }
+ return icons
+}
+
+// Theme represents the color theme of an icon.
+type Theme string
+
+const (
+ // ThemeLight is for light backgrounds (dark/black icons).
+ ThemeLight Theme = "light"
+ // ThemeDark is for dark backgrounds (light/white icons).
+ ThemeDark Theme = "dark"
+)
+
+// DataURI returns a data URI for the embedded Octicon PNG.
+// The theme parameter specifies which variant to use:
+// - ThemeLight: dark icons for light backgrounds
+// - ThemeDark: light icons for dark backgrounds
+// If the icon is not found in the embedded filesystem, it returns an empty string.
+func DataURI(name string, theme Theme) string {
+ filename := fmt.Sprintf("icons/%s-%s.png", name, theme)
+ data, err := iconsFS.ReadFile(filename)
+ if err != nil {
+ return ""
+ }
+ return "data:image/png;base64," + base64.StdEncoding.EncodeToString(data)
+}
+
+// Icons returns MCP Icon objects for the given octicon name in light and dark themes.
+// Icons are embedded as 24x24 PNG data URIs for offline use and faster loading.
+// The name should be the base octicon name without size suffix (e.g., "repo" not "repo-16").
+// See https://primer.style/foundations/icons for available icons.
+func Icons(name string) []mcp.Icon {
+ if name == "" {
+ return nil
+ }
+ return []mcp.Icon{
+ {
+ Source: DataURI(name, ThemeLight),
+ MIMEType: "image/png",
+ Sizes: []string{"24x24"},
+ Theme: string(ThemeLight),
+ },
+ {
+ Source: DataURI(name, ThemeDark),
+ MIMEType: "image/png",
+ Sizes: []string{"24x24"},
+ Theme: string(ThemeDark),
+ },
+ }
+}
diff --git a/pkg/octicons/octicons_test.go b/pkg/octicons/octicons_test.go
new file mode 100644
index 000000000..f60f7192e
--- /dev/null
+++ b/pkg/octicons/octicons_test.go
@@ -0,0 +1,118 @@
+package octicons
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDataURI(t *testing.T) {
+ tests := []struct {
+ name string
+ icon string
+ theme Theme
+ wantDataURI bool
+ wantEmpty bool
+ }{
+ {
+ name: "light theme icon returns data URI",
+ icon: "repo",
+ theme: ThemeLight,
+ wantDataURI: true,
+ wantEmpty: false,
+ },
+ {
+ name: "dark theme icon returns data URI",
+ icon: "repo",
+ theme: ThemeDark,
+ wantDataURI: true,
+ wantEmpty: false,
+ },
+ {
+ name: "non-embedded icon returns empty string",
+ icon: "nonexistent-icon",
+ theme: ThemeLight,
+ wantDataURI: false,
+ wantEmpty: true,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ result := DataURI(tc.icon, tc.theme)
+ if tc.wantDataURI {
+ assert.True(t, strings.HasPrefix(result, "data:image/png;base64,"), "expected data URI prefix")
+ assert.NotContains(t, result, "https://")
+ }
+ if tc.wantEmpty {
+ assert.Empty(t, result, "expected empty string for non-embedded icon")
+ }
+ })
+ }
+}
+
+func TestIcons(t *testing.T) {
+ tests := []struct {
+ name string
+ icon string
+ wantNil bool
+ wantCount int
+ }{
+ {
+ name: "valid embedded icon returns light and dark variants",
+ icon: "repo",
+ wantNil: false,
+ wantCount: 2,
+ },
+ {
+ name: "empty name returns nil",
+ icon: "",
+ wantNil: true,
+ wantCount: 0,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ result := Icons(tc.icon)
+ if tc.wantNil {
+ assert.Nil(t, result)
+ return
+ }
+ assert.NotNil(t, result)
+ assert.Len(t, result, tc.wantCount)
+
+ // Verify first icon is light theme
+ assert.Equal(t, DataURI(tc.icon, ThemeLight), result[0].Source)
+ assert.Equal(t, "image/png", result[0].MIMEType)
+ assert.Equal(t, []string{"24x24"}, result[0].Sizes)
+ assert.Equal(t, "light", result[0].Theme)
+
+ // Verify second icon is dark theme
+ assert.Equal(t, DataURI(tc.icon, ThemeDark), result[1].Source)
+ assert.Equal(t, "image/png", result[1].MIMEType)
+ assert.Equal(t, []string{"24x24"}, result[1].Sizes)
+ assert.Equal(t, "dark", result[1].Theme)
+ })
+ }
+}
+
+func TestThemeConstants(t *testing.T) {
+ assert.Equal(t, Theme("light"), ThemeLight)
+ assert.Equal(t, Theme("dark"), ThemeDark)
+}
+
+func TestEmbeddedIconsExist(t *testing.T) {
+ // Test that all required icons from required_icons.txt are properly embedded
+ // This is the single source of truth for which icons should be available
+ expectedIcons := RequiredIcons()
+ for _, icon := range expectedIcons {
+ t.Run(icon, func(t *testing.T) {
+ lightURI := DataURI(icon, ThemeLight)
+ darkURI := DataURI(icon, ThemeDark)
+ assert.True(t, strings.HasPrefix(lightURI, "data:image/png;base64,"), "light theme icon %s should be embedded", icon)
+ assert.True(t, strings.HasPrefix(darkURI, "data:image/png;base64,"), "dark theme icon %s should be embedded", icon)
+ })
+ }
+}
diff --git a/pkg/octicons/required_icons.txt b/pkg/octicons/required_icons.txt
new file mode 100644
index 000000000..7911b46eb
--- /dev/null
+++ b/pkg/octicons/required_icons.txt
@@ -0,0 +1,45 @@
+# Required Octicons for the GitHub MCP Server
+# This file is the source of truth for icon requirements.
+# Used by:
+# - script/fetch-icons (to download icons)
+# - pkg/octicons/octicons_test.go (to validate icons are embedded)
+# - pkg/github/toolset_icons_test.go (to validate toolset icons exist)
+#
+# Add new icons here when:
+# - Adding a new toolset with an icon
+# - Adding a new tool that needs a custom icon
+#
+# Format: one icon name per line (without -24.svg suffix)
+# Lines starting with # are comments
+# Empty lines are ignored
+
+apps
+beaker
+bell
+book
+check-circle
+codescan
+comment-discussion
+copilot
+dependabot
+file
+git-branch
+git-commit
+git-merge
+git-pull-request
+issue-opened
+logo-gist
+mark-github
+organization
+people
+person
+project
+repo
+repo-forked
+shield
+shield-lock
+star
+star-fill
+tag
+tools
+workflow
diff --git a/script/fetch-icons b/script/fetch-icons
new file mode 100755
index 000000000..21de625f1
--- /dev/null
+++ b/script/fetch-icons
@@ -0,0 +1,72 @@
+#!/bin/bash
+# Fetch Octicon icons and convert them to PNG for embedding in the MCP server.
+# Generates both light theme (dark icons) and dark theme (white icons) variants.
+# Uses sed to modify SVG fill color before converting to PNG.
+# Requires: rsvg-convert (from librsvg2-bin on Ubuntu/Debian)
+#
+# Usage:
+# script/fetch-icons # Fetch all required icons
+# script/fetch-icons icon1 icon2 # Fetch specific icons
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+ICONS_DIR="$REPO_ROOT/pkg/octicons/icons"
+REQUIRED_ICONS_FILE="$REPO_ROOT/pkg/octicons/required_icons.txt"
+OCTICONS_BASE="https://raw.githubusercontent.com/primer/octicons/main/icons"
+
+# Check for rsvg-convert
+if ! command -v rsvg-convert &> /dev/null; then
+ echo "Error: rsvg-convert not found. Install with:"
+ echo " Ubuntu/Debian: sudo apt-get install librsvg2-bin"
+ echo " macOS: brew install librsvg"
+ exit 1
+fi
+
+# Load icons from required_icons.txt or use command-line arguments
+if [ $# -gt 0 ]; then
+ ICONS=("$@")
+else
+ if [ ! -f "$REQUIRED_ICONS_FILE" ]; then
+ echo "Error: Required icons file not found: $REQUIRED_ICONS_FILE"
+ exit 1
+ fi
+ # Read icons from file, skipping comments and empty lines
+ mapfile -t ICONS < <(grep -v '^#' "$REQUIRED_ICONS_FILE" | grep -v '^$')
+fi
+
+# Ensure icons directory exists
+mkdir -p "$ICONS_DIR"
+
+echo "Fetching ${#ICONS[@]} icons (24px, light + dark themes)..."
+
+for icon in "${ICONS[@]}"; do
+ svg_url="${OCTICONS_BASE}/${icon}-24.svg"
+ light_file="${ICONS_DIR}/${icon}-light.png"
+ dark_file="${ICONS_DIR}/${icon}-dark.png"
+
+ echo " ${icon} (light + dark)"
+
+ # Download SVG
+ svg_content=$(curl -sfL "$svg_url" 2>/dev/null) || {
+ echo " Warning: Failed to fetch ${icon}-24.svg (may not exist)"
+ continue
+ }
+
+ # Light theme: dark icons (#24292f) for light backgrounds
+ # Add fill attribute to the svg tag
+ light_svg=$(echo "$svg_content" | sed 's/