@@ -36,7 +36,7 @@ func generateAllDocs() error {
3636 // File to edit, function to generate its docs
3737 {"README.md" , generateReadmeDocs },
3838 {"docs/remote-server.md" , generateRemoteServerDocs },
39- {"docs/deprecated- tool-aliases .md" , generateDeprecatedAliasesDocs },
39+ {"docs/tool-renaming .md" , generateDeprecatedAliasesDocs },
4040 } {
4141 if err := doc .fn (doc .path ); err != nil {
4242 return fmt .Errorf ("failed to generate docs for %s: %w" , doc .path , err )
@@ -101,23 +101,52 @@ func generateRemoteServerDocs(docsPath string) error {
101101 return err
102102 }
103103
104+ // Also generate remote-only toolsets section
105+ remoteOnlyDoc := generateRemoteOnlyToolsetsDoc ()
106+ updatedContent , err = replaceSection (updatedContent , "START AUTOMATED REMOTE TOOLSETS" , "END AUTOMATED REMOTE TOOLSETS" , remoteOnlyDoc )
107+ if err != nil {
108+ return err
109+ }
110+
104111 return os .WriteFile (docsPath , []byte (updatedContent ), 0600 ) //#nosec G306
105112}
106113
114+ // octiconImg returns an img tag for an Octicon that works with GitHub's light/dark theme.
115+ // Uses picture element with prefers-color-scheme for automatic theme switching.
116+ // References icons from the repo's pkg/octicons/icons directory.
117+ // Optional pathPrefix for files in subdirectories (e.g., "../" for docs/).
118+ func octiconImg (name string , pathPrefix ... string ) string {
119+ if name == "" {
120+ return ""
121+ }
122+ prefix := ""
123+ if len (pathPrefix ) > 0 {
124+ prefix = pathPrefix [0 ]
125+ }
126+ // Use picture element with media queries for light/dark mode support
127+ // GitHub renders these correctly in markdown
128+ lightIcon := fmt .Sprintf ("%spkg/octicons/icons/%s-light.png" , prefix , name )
129+ darkIcon := fmt .Sprintf ("%spkg/octicons/icons/%s-dark.png" , prefix , name )
130+ return fmt .Sprintf (`<picture><source media="(prefers-color-scheme: dark)" srcset="%s"><source media="(prefers-color-scheme: light)" srcset="%s"><img src="%s" width="20" height="20" alt="%s"></picture>` , darkIcon , lightIcon , lightIcon , name )
131+ }
132+
107133func generateToolsetsDoc (i * inventory.Inventory ) string {
108134 var buf strings.Builder
109135
110- // Add table header and separator
111- buf .WriteString ("| Toolset | Description |\n " )
112- buf .WriteString ("| ----------------------- | ------------------------------------------------------------- |\n " )
136+ // Add table header and separator (with icon column)
137+ buf .WriteString ("| | Toolset | Description |\n " )
138+ buf .WriteString ("| --- | --- -------------------- | ------------------------------------------------------------- |\n " )
113139
114140 // Add the context toolset row with custom description (strongly recommended)
115- buf .WriteString ("| `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |\n " )
141+ // Get context toolset for its icon
142+ contextIcon := octiconImg ("person" )
143+ fmt .Fprintf (& buf , "| %s | `context` | **Strongly recommended**: Tools that provide context about the current user and GitHub context you are operating in |\n " , contextIcon )
116144
117145 // AvailableToolsets() returns toolsets that have tools, sorted by ID
118146 // Exclude context (custom description above) and dynamic (internal only)
119147 for _ , ts := range i .AvailableToolsets ("context" , "dynamic" ) {
120- fmt .Fprintf (& buf , "| `%s` | %s |\n " , ts .ID , ts .Description )
148+ icon := octiconImg (ts .Icon )
149+ fmt .Fprintf (& buf , "| %s | `%s` | %s |\n " , icon , ts .ID , ts .Description )
121150 }
122151
123152 return strings .TrimSuffix (buf .String (), "\n " )
@@ -134,6 +163,7 @@ func generateToolsDoc(r *inventory.Inventory) string {
134163 var buf strings.Builder
135164 var toolBuf strings.Builder
136165 var currentToolsetID inventory.ToolsetID
166+ var currentToolsetIcon string
137167 firstSection := true
138168
139169 writeSection := func () {
@@ -145,7 +175,11 @@ func generateToolsDoc(r *inventory.Inventory) string {
145175 }
146176 firstSection = false
147177 sectionName := formatToolsetName (string (currentToolsetID ))
148- fmt .Fprintf (& buf , "<details>\n \n <summary>%s</summary>\n \n %s\n \n </details>" , sectionName , strings .TrimSuffix (toolBuf .String (), "\n \n " ))
178+ icon := octiconImg (currentToolsetIcon )
179+ if icon != "" {
180+ icon += " "
181+ }
182+ fmt .Fprintf (& buf , "<details>\n \n <summary>%s%s</summary>\n \n %s\n \n </details>" , icon , sectionName , strings .TrimSuffix (toolBuf .String (), "\n \n " ))
149183 toolBuf .Reset ()
150184 }
151185
@@ -154,6 +188,7 @@ func generateToolsDoc(r *inventory.Inventory) string {
154188 if tool .Toolset .ID != currentToolsetID {
155189 writeSection ()
156190 currentToolsetID = tool .Toolset .ID
191+ currentToolsetIcon = tool .Toolset .Icon
157192 }
158193 writeToolDoc (& toolBuf , tool .Tool )
159194 toolBuf .WriteString ("\n \n " )
@@ -190,7 +225,7 @@ func formatToolsetName(name string) string {
190225}
191226
192227func writeToolDoc (buf * strings.Builder , tool mcp.Tool ) {
193- // Tool name only (using annotation name instead of verbose description )
228+ // Tool name (no icon - section header already has the toolset icon )
194229 fmt .Fprintf (buf , "- **%s** - %s\n " , tool .Name , tool .Annotations .Title )
195230
196231 // Parameters
@@ -302,12 +337,13 @@ func generateRemoteToolsetsDoc() string {
302337 // Build inventory - stateless
303338 r := github .NewInventory (t ).Build ()
304339
305- // Generate table header
306- buf .WriteString ("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n " )
307- buf .WriteString ("|----------------| --------------------------------------------------| -------------------------------------------------------| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ---------------------------------------------------------------------------------------------------------------| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n " )
340+ // Generate table header (icon is combined with Name column)
341+ buf .WriteString ("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n " )
342+ buf .WriteString ("| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |\n " )
308343
309344 // Add "all" toolset first (special case)
310- 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 " )
345+ allIcon := octiconImg ("apps" , "../" )
346+ fmt .Fprintf (& buf , "| %s<br>all | All available GitHub MCP tools | https://api.githubcopilot.com/mcp/ | [Install](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%%7B%%22type%%22%%3A%%20%%22http%%22%%2C%%22url%%22%%3A%%20%%22https%%3A%%2F%%2Fapi.githubcopilot.com%%2Fmcp%%2F%%22%%7D) | [read-only](https://api.githubcopilot.com/mcp/readonly) | [Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=github&config=%%7B%%22type%%22%%3A%%20%%22http%%22%%2C%%22url%%22%%3A%%20%%22https%%3A%%2F%%2Fapi.githubcopilot.com%%2Fmcp%%2Freadonly%%22%%7D) |\n " , allIcon )
311347
312348 // AvailableToolsets() returns toolsets that have tools, sorted by ID
313349 // Exclude context (handled separately) and dynamic (internal only)
@@ -329,19 +365,61 @@ func generateRemoteToolsetsDoc() string {
329365 installLink := fmt .Sprintf ("[Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)" , idStr , installConfig )
330366 readonlyInstallLink := fmt .Sprintf ("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)" , idStr , readonlyConfig )
331367
332- fmt .Fprintf (& buf , "| %-14s | %-48s | %-53s | %-218s | %-110s | %-288s |\n " ,
368+ icon := octiconImg (ts .Icon , "../" )
369+ fmt .Fprintf (& buf , "| %s<br>%s | %s | %s | %s | [read-only](%s) | %s |\n " ,
370+ icon ,
333371 formattedName ,
334372 ts .Description ,
335373 apiURL ,
336374 installLink ,
337- fmt . Sprintf ( "[read-only](%s)" , readonlyURL ) ,
375+ readonlyURL ,
338376 readonlyInstallLink ,
339377 )
340378 }
341379
342380 return strings .TrimSuffix (buf .String (), "\n " )
343381}
344382
383+ func generateRemoteOnlyToolsetsDoc () string {
384+ var buf strings.Builder
385+
386+ // Generate table header (icon is combined with Name column)
387+ buf .WriteString ("| Name | Description | API URL | 1-Click Install (VS Code) | Read-only Link | 1-Click Read-only Install (VS Code) |\n " )
388+ buf .WriteString ("| ---- | ----------- | ------- | ------------------------- | -------------- | ----------------------------------- |\n " )
389+
390+ // Use RemoteOnlyToolsets from github package
391+ for _ , ts := range github .RemoteOnlyToolsets () {
392+ idStr := string (ts .ID )
393+
394+ formattedName := formatToolsetName (idStr )
395+ apiURL := fmt .Sprintf ("https://api.githubcopilot.com/mcp/x/%s" , idStr )
396+ readonlyURL := fmt .Sprintf ("https://api.githubcopilot.com/mcp/x/%s/readonly" , idStr )
397+
398+ // Create install config JSON (URL encoded)
399+ installConfig := url .QueryEscape (fmt .Sprintf (`{"type": "http","url": "%s"}` , apiURL ))
400+ readonlyConfig := url .QueryEscape (fmt .Sprintf (`{"type": "http","url": "%s"}` , readonlyURL ))
401+
402+ // Fix URL encoding to use %20 instead of + for spaces
403+ installConfig = strings .ReplaceAll (installConfig , "+" , "%20" )
404+ readonlyConfig = strings .ReplaceAll (readonlyConfig , "+" , "%20" )
405+
406+ installLink := fmt .Sprintf ("[Install](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)" , idStr , installConfig )
407+ readonlyInstallLink := fmt .Sprintf ("[Install read-only](https://insiders.vscode.dev/redirect/mcp/install?name=gh-%s&config=%s)" , idStr , readonlyConfig )
408+
409+ icon := octiconImg (ts .Icon , "../" )
410+ fmt .Fprintf (& buf , "| %s<br>%s | %s | %s | %s | [read-only](%s) | %s |\n " ,
411+ icon ,
412+ formattedName ,
413+ ts .Description ,
414+ apiURL ,
415+ installLink ,
416+ readonlyURL ,
417+ readonlyInstallLink ,
418+ )
419+ }
420+
421+ return strings .TrimSuffix (buf .String (), "\n " )
422+ }
345423func generateDeprecatedAliasesDocs (docsPath string ) error {
346424 // Read the current file
347425 content , err := os .ReadFile (docsPath ) //#nosec G304
0 commit comments