Skip to content

Commit f8cc2f4

Browse files
docs(insiders): auto-generate per-flag tool change list
Adds a generated section to docs/insiders-features.md that lists every tool added or modified by each entry in InsidersFeatureFlags. The section is computed by diffing the default-flagged inventory against the per-flag inventory at doc-generation time, so any new tool gated behind an insiders flag — or any flag-gated schema change — shows up without manual edits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6fd9d07 commit f8cc2f4

3 files changed

Lines changed: 188 additions & 0 deletions

File tree

cmd/github-mcp-server/generate_docs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func generateAllDocs() error {
4343
// File to edit, function to generate its docs
4444
{"README.md", generateReadmeDocs},
4545
{"docs/remote-server.md", generateRemoteServerDocs},
46+
{"docs/insiders-features.md", generateInsidersFeaturesDocs},
4647
{"docs/tool-renaming.md", generateDeprecatedAliasesDocs},
4748
} {
4849
if err := doc.fn(doc.path); err != nil {
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"reflect"
8+
"sort"
9+
"strings"
10+
11+
"github.com/github/github-mcp-server/pkg/github"
12+
"github.com/github/github-mcp-server/pkg/inventory"
13+
"github.com/github/github-mcp-server/pkg/translations"
14+
)
15+
16+
// generateInsidersFeaturesDocs refreshes the auto-generated section of
17+
// docs/insiders-features.md with the list of tools that become available, or
18+
// change shape, when Insiders Mode is enabled. The list is computed by diffing
19+
// the default-flagged inventory against the per-flag insiders inventory so it
20+
// stays in sync with the Go source without manual edits.
21+
func generateInsidersFeaturesDocs(docsPath string) error {
22+
content, err := os.ReadFile(docsPath) //#nosec G304
23+
if err != nil {
24+
return fmt.Errorf("failed to read docs file: %w", err)
25+
}
26+
27+
body := generateInsidersToolsDoc()
28+
29+
updatedContent, err := replaceSection(string(content), "START AUTOMATED INSIDERS TOOLS", "END AUTOMATED INSIDERS TOOLS", body)
30+
if err != nil {
31+
return err
32+
}
33+
34+
return os.WriteFile(docsPath, []byte(updatedContent), 0600) //#nosec G306
35+
}
36+
37+
// generateInsidersToolsDoc renders the per-flag breakdown of tools that
38+
// Insiders Mode adds or modifies relative to the default user experience.
39+
func generateInsidersToolsDoc() string {
40+
t, _ := translations.TranslationHelper()
41+
42+
defaults := buildInventoryWithFlags(t, nil)
43+
defaultTools := indexToolsByName(defaults.AvailableTools(context.Background()))
44+
45+
var buf strings.Builder
46+
hasAny := false
47+
48+
for _, flag := range github.InsidersFeatureFlags {
49+
entries := insidersFlagEntries(t, flag, defaultTools)
50+
if len(entries) == 0 {
51+
continue
52+
}
53+
54+
if hasAny {
55+
buf.WriteString("\n\n")
56+
}
57+
hasAny = true
58+
59+
fmt.Fprintf(&buf, "### `%s`\n\n", flag)
60+
buf.WriteString("| Tool | Change |\n")
61+
buf.WriteString("|------|--------|\n")
62+
for _, e := range entries {
63+
fmt.Fprintf(&buf, "| `%s` | %s |\n", e.name, e.change)
64+
}
65+
}
66+
67+
if !hasAny {
68+
return "_No Insiders-only tool changes._"
69+
}
70+
71+
return strings.TrimSuffix(buf.String(), "\n")
72+
}
73+
74+
type insidersEntry struct {
75+
name string
76+
change string
77+
}
78+
79+
// insidersFlagEntries returns the per-tool deltas introduced when a single
80+
// insiders flag is enabled on top of the default-flagged inventory. Entries
81+
// are sorted by tool name for deterministic output.
82+
func insidersFlagEntries(t translations.TranslationHelperFunc, flag string, defaultTools map[string]inventory.ServerTool) []insidersEntry {
83+
withFlag := buildInventoryWithFlags(t, map[string]bool{flag: true})
84+
flagTools := withFlag.AvailableTools(context.Background())
85+
86+
entries := make([]insidersEntry, 0)
87+
seen := make(map[string]struct{}, len(flagTools))
88+
89+
for _, tool := range flagTools {
90+
if _, ok := seen[tool.Tool.Name]; ok {
91+
continue
92+
}
93+
seen[tool.Tool.Name] = struct{}{}
94+
95+
baseline, hadBaseline := defaultTools[tool.Tool.Name]
96+
change := describeInsidersChange(flag, tool, baseline, hadBaseline)
97+
if change == "" {
98+
continue
99+
}
100+
entries = append(entries, insidersEntry{name: tool.Tool.Name, change: change})
101+
}
102+
103+
sort.Slice(entries, func(i, j int) bool { return entries[i].name < entries[j].name })
104+
return entries
105+
}
106+
107+
// describeInsidersChange classifies the difference between a flag-on tool
108+
// variant and its (possibly absent) default-flagged counterpart. Returns an
109+
// empty string when the tool is unchanged.
110+
func describeInsidersChange(flag string, flagTool, baseline inventory.ServerTool, hadBaseline bool) string {
111+
if !hadBaseline {
112+
return "New tool"
113+
}
114+
if flagTool.FeatureFlagEnable == flag {
115+
return "Schema changes (input or output) under this flag"
116+
}
117+
if flag == github.MCPAppsFeatureFlag && hasMCPAppsUIMeta(flagTool) {
118+
return "Adds MCP Apps UI metadata for interactive rendering"
119+
}
120+
if !reflect.DeepEqual(flagTool.Tool.InputSchema, baseline.Tool.InputSchema) {
121+
return "Input schema differs from the default variant"
122+
}
123+
return ""
124+
}
125+
126+
// hasMCPAppsUIMeta reports whether the tool declares the MCP Apps UI metadata
127+
// key on its Tool.Meta map. The strip happens at registration time, so the
128+
// metadata is still present on the inventory's tool definitions.
129+
func hasMCPAppsUIMeta(tool inventory.ServerTool) bool {
130+
if tool.Tool.Meta == nil {
131+
return false
132+
}
133+
_, ok := tool.Tool.Meta["ui"]
134+
return ok
135+
}
136+
137+
// buildInventoryWithFlags constructs an inventory whose feature checker treats
138+
// the given flags as enabled and every other flag as disabled. Passing nil
139+
// produces the default-flagged inventory.
140+
func buildInventoryWithFlags(t translations.TranslationHelperFunc, enabled map[string]bool) *inventory.Inventory {
141+
checker := func(_ context.Context, flag string) (bool, error) {
142+
return enabled[flag], nil
143+
}
144+
inv, _ := github.NewInventory(t).
145+
WithToolsets([]string{"all"}).
146+
WithFeatureChecker(checker).
147+
Build()
148+
return inv
149+
}
150+
151+
// indexToolsByName returns a map keyed by tool name. When duplicates exist
152+
// (e.g. flag-gated dual registrations), the first occurrence wins, mirroring
153+
// AvailableTools' deterministic sort order.
154+
func indexToolsByName(tools []inventory.ServerTool) map[string]inventory.ServerTool {
155+
out := make(map[string]inventory.ServerTool, len(tools))
156+
for _, tool := range tools {
157+
if _, ok := out[tool.Tool.Name]; ok {
158+
continue
159+
}
160+
out[tool.Tool.Name] = tool
161+
}
162+
return out
163+
}

docs/insiders-features.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ For configuration examples, see the [Server Configuration Guide](./server-config
2020

2121
---
2222

23+
## Tools added or changed by Insiders Mode
24+
25+
The following table is generated from the Go source — it lists every tool that becomes available, or changes shape, when an Insiders feature flag is enabled. Each section is keyed by the underlying flag so contributors can see which flag is responsible for what.
26+
27+
<!-- START AUTOMATED INSIDERS TOOLS -->
28+
### `remote_mcp_ui_apps`
29+
30+
| Tool | Change |
31+
|------|--------|
32+
| `create_pull_request` | Adds MCP Apps UI metadata for interactive rendering |
33+
| `get_me` | Adds MCP Apps UI metadata for interactive rendering |
34+
| `issue_write` | Adds MCP Apps UI metadata for interactive rendering |
35+
36+
37+
### `remote_mcp_issue_fields`
38+
39+
| Tool | Change |
40+
|------|--------|
41+
| `list_issue_fields` | New tool |
42+
| `list_issues` | Schema changes (input or output) under this flag |
43+
<!-- END AUTOMATED INSIDERS TOOLS -->
44+
45+
---
46+
2347
## MCP Apps
2448

2549
[MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) is an extension to the Model Context Protocol that enables servers to deliver interactive user interfaces to end users. Instead of returning plain text that the LLM must interpret and relay, tools can render forms, profiles, and dashboards right in the chat using MCP Apps.

0 commit comments

Comments
 (0)