Migrate MCP Apps support from insiders mode to feature flag with insiders opt-in#2282
Migrate MCP Apps support from insiders mode to feature flag with insiders opt-in#2282mattdholloway wants to merge 7 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates MCP Apps (interactive UI forms) enablement from “insiders mode” to an explicit feature flag (remote_mcp_ui_apps), while keeping backward compatibility by treating insiders mode as implying the flag.
Changes:
- Introduces
github.MCPAppsFeatureFlag(remote_mcp_ui_apps) and wires it through stdio/HTTP feature checking with an insiders-mode transitional fallback. - Adds inventory builder support (
WithMCPApps) to strip/keep MCP Apps UI metadata (Meta["ui"]) based on the feature flag. - Updates tool behavior gates and documentation to reference the feature flag instead of insiders mode.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/inventory/registry_test.go | Renames/extends tests for MCP Apps gating and meta stripping; adds stripMetaKeys coverage. |
| pkg/inventory/builder.go | Adds WithMCPApps, separates insiders stripping from MCP Apps UI metadata stripping, and introduces stripMetaKeys. |
| pkg/http/server.go | Whitelists remote_mcp_ui_apps for X-MCP-Features and adds transitional insiders behavior in feature checker. |
| pkg/http/handler.go | Applies WithInsidersMode and WithMCPApps to the inventory builder based on request context/header features. |
| pkg/github/pullrequests.go | Gates PR UI form behavior on remote_mcp_ui_apps feature flag instead of insiders mode. |
| pkg/github/pullrequests_test.go | Updates UI-gate test setup to use feature checker-based deps rather than InsidersMode. |
| pkg/github/issues.go | Gates issue UI form behavior on remote_mcp_ui_apps feature flag instead of insiders mode. |
| pkg/github/issues_test.go | Updates UI-gate test setup to use feature checker-based deps rather than InsidersMode. |
| pkg/github/feature_flags.go | Adds the exported MCPAppsFeatureFlag constant. |
| internal/ghmcp/server.go | Ensures stdio “insiders mode” also enables remote_mcp_ui_apps; registers UI resources based on the flag. |
| docs/server-configuration.md | Documents feature flags and MCP Apps configuration via X-MCP-Features / --features. |
| docs/insiders-features.md | Removes MCP Apps from insiders-only list; points to feature-flag configuration. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates MCP Apps enablement from being tied to insiders mode to a dedicated feature flag (remote_mcp_ui_apps), while preserving backward compatibility by having insiders mode imply the feature flag.
Changes:
- Introduces
github.MCPAppsFeatureFlagand updates tool handlers (issue_write,create_pull_request) to gate MCP Apps UI behavior via feature flag checks. - Adds transport-specific feature enablement: HTTP via
X-MCP-Features(whitelisted) and stdio via--features, with insiders-mode implication during the transition. - Updates inventory building to strip
uimetadata unless MCP Apps is enabled, and refreshes related tests and docs.
Show a summary per file
| File | Description |
|---|---|
| pkg/github/feature_flags.go | Defines the remote_mcp_ui_apps feature flag constant and documents its purpose. |
| pkg/github/issues.go | Switches UI-gate logic from insiders mode to the MCP Apps feature flag. |
| pkg/github/issues_test.go | Updates UI-gate tests to use feature checker-based enablement instead of insiders mode. |
| pkg/github/pullrequests.go | Switches UI-gate logic from insiders mode to the MCP Apps feature flag. |
| pkg/github/pullrequests_test.go | Updates UI-gate tests to use feature checker-based enablement instead of insiders mode. |
| pkg/http/server.go | Whitelists remote_mcp_ui_apps for X-MCP-Features and adds insiders→flag transitional behavior in the HTTP feature checker. |
| pkg/http/handler.go | Enables MCP Apps inventory behavior based on X-MCP-Features or insiders mode (transitional). |
| pkg/inventory/builder.go | Adds WithMCPApps and strips ui metadata by default unless MCP Apps is enabled; generalizes meta stripping via stripMetaKeys. |
| pkg/inventory/registry_test.go | Renames/adds tests around MCP Apps meta stripping and stripMetaKeys. |
| internal/ghmcp/server.go | Ensures stdio mode treats insiders as implying the feature flag and registers UI resources only when the flag is enabled. |
| docs/server-configuration.md | Documents feature flags and adds MCP Apps configuration examples using X-MCP-Features / --features. |
| docs/insiders-features.md | Removes MCP Apps from insiders-only docs and notes there are currently no insiders-only features. |
Copilot's findings
Comments suppressed due to low confidence (1)
pkg/inventory/registry_test.go:2142
- This test uses
require.Nil(result.Tool.Meta["ui"])(and similar) to assert key stripping, but a map lookup returns nil both when the key is missing and when the key exists with a nil value. To ensurestripMetaKeysactually removes the keys, assert map membership (_, ok := meta["ui"]) rather than only the value.
require.Nil(t, result.Tool.Meta["ui"], "ui should be stripped")
require.Nil(t, result.Tool.Meta["experimental_feature"], "experimental_feature should be stripped")
require.Nil(t, result.Tool.Meta["beta"], "beta should be stripped")
require.Equal(t, "kept", result.Tool.Meta["description"], "description should be preserved")
- Files reviewed: 12/12 changed files
- Comments generated: 2
SamMorrowDrums
left a comment
There was a problem hiding this comment.
Generally happy with this, but it might be nice to avoid the complexity of insiders only and just use feature flags for everything. Insiders would just auto enable a list of feature flags, and all changes would propagate as a consequence.
| } | ||
|
|
||
| // WithMCPApps enables or disables MCP Apps UI features. | ||
| // When disabled (default), the "ui" Meta key is stripped from tools |
There was a problem hiding this comment.
This can also be done via feature flag.
There was a problem hiding this comment.
Pull request overview
This pull request migrates MCP Apps (interactive UI forms) enablement from being tied to Insiders mode to a dedicated feature flag (remote_mcp_ui_apps), while keeping Insiders mode as a backwards-compatible opt-in that implies the flag.
Changes:
- Introduces
remote_mcp_ui_appsas the canonical MCP Apps feature flag and defines which flags Insiders mode implies. - Updates inventory building to include/strip MCP Apps UI metadata based on the feature flag (
WithMCPApps) rather than Insiders mode. - Updates HTTP and stdio server paths, tool handlers, tests, and docs to use the feature flag for MCP Apps UI gating.
Show a summary per file
| File | Description |
|---|---|
| pkg/inventory/server_tool.go | Removes the InsidersOnly tool field as MCP Apps gating is no longer tied to Insiders-only tooling. |
| pkg/inventory/registry_test.go | Renames/updates tests to validate MCP Apps UI metadata stripping/preservation via WithMCPApps and key-stripping helpers. |
| pkg/inventory/builder.go | Adds WithMCPApps and updates build-time stripping logic to remove MCP Apps UI metadata when the flag is not enabled. |
| pkg/http/server.go | Whitelists remote_mcp_ui_apps for X-MCP-Features and expands feature checking to treat Insiders-implied flags as enabled. |
| pkg/http/handler.go | Enables MCP Apps inventory UI behavior for requests when the feature is present (or Insiders mode is active). |
| pkg/github/pullrequests.go | Switches MCP Apps UI gating for create_pull_request from Insiders mode to the feature flag. |
| pkg/github/pullrequests_test.go | Updates tests to enable MCP Apps via a feature checker rather than Insiders mode flags. |
| pkg/github/issues.go | Switches MCP Apps UI gating for issue_write from Insiders mode to the feature flag. |
| pkg/github/issues_test.go | Updates tests to enable MCP Apps via a feature checker rather than Insiders mode flags. |
| pkg/github/feature_flags.go | Adds MCPAppsFeatureFlag constant and InsidersFeatureFlags list as the expansion set for Insiders mode. |
| internal/ghmcp/server.go | Expands enabled features when Insiders mode is on, derives MCP Apps enablement from feature flags, and gates UI resource registration accordingly. |
| docs/server-configuration.md | Documents X-MCP-Features / --features and adds MCP Apps section describing feature-flag-based enablement. |
| docs/insiders-features.md | Removes MCP Apps from insiders-only docs and notes there are currently no insiders-only features. |
Copilot's findings
- Files reviewed: 13/13 changed files
- Comments generated: 1
| // Enable MCP Apps if the feature flag is present in the request headers | ||
| // or if insiders mode is active (insiders expands InsidersFeatureFlags). | ||
| headerFeatures := ghcontext.GetHeaderFeatures(ctx) | ||
| mcpApps := slices.Contains(headerFeatures, github.MCPAppsFeatureFlag) || ghcontext.IsInsidersMode(ctx) | ||
| if mcpApps { | ||
| builder = builder.WithMCPApps(true) | ||
| } |
There was a problem hiding this comment.
InventoryFiltersForRequest enables MCP Apps UI metadata purely based on the raw header value or Insiders mode, without consulting the same sources of truth used elsewhere (the X-MCP-Features whitelist in pkg/http/server.go and github.InsidersFeatureFlags). This can drift over time (e.g., if MCPAppsFeatureFlag is removed from the whitelist or from InsidersFeatureFlags, the inventory could still include UI metadata even though the feature checker would treat it as disabled). Consider deriving mcpApps from the whitelist + header check and from github.InsidersFeatureFlags membership when Insiders mode is active, so inventory/UI metadata gating stays consistent with feature-flag evaluation.
There was a problem hiding this comment.
Pull request overview
This PR migrates MCP Apps support from being implicitly tied to “insiders mode” to being controlled by an explicit feature flag (remote_mcp_ui_apps), while keeping insiders mode as an opt-in that expands to a set of feature flags.
Changes:
- Introduces
MCPAppsFeatureFlagandInsidersFeatureFlags, and updates feature-checking to treat insiders mode as feature-flag expansion. - Updates inventory building and tool behavior to gate MCP Apps UI behavior/metadata behind the MCP Apps feature flag (instead of hard-coded insiders checks).
- Updates docs and tests to reflect the new feature-flag-based enablement.
Show a summary per file
| File | Description |
|---|---|
| pkg/inventory/server_tool.go | Removes InsidersOnly from ServerTool, relying on feature-flag gating instead. |
| pkg/inventory/builder.go | Replaces insiders-mode stripping with MCP Apps UI metadata stripping via WithMCPApps and stripMetaKeys. |
| pkg/inventory/registry_test.go | Updates/renames tests to validate MCP Apps UI metadata stripping behavior. |
| pkg/http/server.go | Whitelists remote_mcp_ui_apps for X-MCP-Features and expands insiders mode to enable insiders flags. |
| pkg/http/handler.go | Enables MCP Apps inventory behavior based on request feature flag / insiders mode. |
| pkg/github/feature_flags.go | Adds the MCP Apps feature flag constant and insiders expansion list. |
| pkg/github/issues.go | Gates issue UI-form behavior via MCP Apps feature flag checks. |
| pkg/github/issues_test.go | Updates tests to use feature-checker-based gating rather than insiders mode. |
| pkg/github/pullrequests.go | Gates PR UI-form behavior via MCP Apps feature flag checks. |
| pkg/github/pullrequests_test.go | Updates tests to use feature-checker-based gating rather than insiders mode. |
| internal/ghmcp/server.go | Expands enabled features when insiders mode is set; enables MCP Apps inventory + UI resource registration based on the flag. |
| docs/server-configuration.md | Documents MCP Apps as a feature flag (and via insiders), with configuration examples. |
Copilot's findings
- Files reviewed: 12/12 changed files
- Comments generated: 3
| @@ -82,10 +82,6 @@ type ServerTool struct { | |||
| // This includes the required scopes plus any higher-level scopes that provide | |||
| // the necessary permissions due to scope hierarchy. | |||
| AcceptedScopes []string | |||
There was a problem hiding this comment.
Removing the exported InsidersOnly field from inventory.ServerTool is an API-breaking change for any external consumers that compile against this module. If the intent is only to stop using insiders-only tool filtering internally, consider keeping the field (deprecated / no-op) for backward compatibility, or ensure the change is explicitly handled as a breaking change (e.g., major version bump + migration notes).
| AcceptedScopes []string | |
| AcceptedScopes []string | |
| // Deprecated: InsidersOnly is kept for backward compatibility with external | |
| // consumers that still compile against this field. It is no longer used for | |
| // tool filtering and has no effect. | |
| InsidersOnly bool |
| // MCPAppsFeatureFlag is the feature flag name for MCP Apps (interactive UI forms). | ||
| const MCPAppsFeatureFlag = "remote_mcp_ui_apps" | ||
|
|
||
| // InsidersFeatureFlags is the list of feature flags that insiders mode enables. | ||
| // When insiders mode is active, all flags in this list are treated as enabled. | ||
| // This is the single source of truth for what "insiders" means in terms of | ||
| // feature flag expansion. | ||
| var InsidersFeatureFlags = []string{ | ||
| MCPAppsFeatureFlag, | ||
| } | ||
|
|
There was a problem hiding this comment.
InsidersFeatureFlags is an exported, mutable slice; downstream packages could modify it at runtime and unintentionally change feature-gating behavior. To prevent accidental mutation, consider making this unexported and exposing an accessor that returns a cloned slice (or returning a set/map), so insiders expansion remains a stable single source of truth.
| // MCPAppsFeatureFlag is the feature flag name for MCP Apps (interactive UI forms). | |
| const MCPAppsFeatureFlag = "remote_mcp_ui_apps" | |
| // InsidersFeatureFlags is the list of feature flags that insiders mode enables. | |
| // When insiders mode is active, all flags in this list are treated as enabled. | |
| // This is the single source of truth for what "insiders" means in terms of | |
| // feature flag expansion. | |
| var InsidersFeatureFlags = []string{ | |
| MCPAppsFeatureFlag, | |
| } | |
| import "slices" | |
| // MCPAppsFeatureFlag is the feature flag name for MCP Apps (interactive UI forms). | |
| const MCPAppsFeatureFlag = "remote_mcp_ui_apps" | |
| // insidersFeatureFlags is the list of feature flags that insiders mode enables. | |
| // When insiders mode is active, all flags in this list are treated as enabled. | |
| // This is the single source of truth for what "insiders" means in terms of | |
| // feature flag expansion. | |
| var insidersFeatureFlags = []string{ | |
| MCPAppsFeatureFlag, | |
| } | |
| // InsidersFeatureFlags returns the feature flags that insiders mode enables. | |
| func InsidersFeatureFlags() []string { | |
| return slices.Clone(insidersFeatureFlags) | |
| } |
| // or if insiders mode is active (insiders expands InsidersFeatureFlags). | ||
| headerFeatures := ghcontext.GetHeaderFeatures(ctx) | ||
| mcpApps := slices.Contains(headerFeatures, github.MCPAppsFeatureFlag) || ghcontext.IsInsidersMode(ctx) |
There was a problem hiding this comment.
HTTP mode now preserves tool Meta["ui"] when remote_mcp_ui_apps is enabled (via WithMCPApps(true)), but the HTTP server path does not appear to register the corresponding UI resources (e.g., github.RegisterUIResources) anywhere. This can lead to UI-capable clients receiving resourceUri values that the server can’t resolve. Consider registering UI resources when MCP Apps is enabled (and github.UIAssetsAvailable()), or stripping/omitting the UI meta unless resources are actually available in HTTP mode too.
| // or if insiders mode is active (insiders expands InsidersFeatureFlags). | |
| headerFeatures := ghcontext.GetHeaderFeatures(ctx) | |
| mcpApps := slices.Contains(headerFeatures, github.MCPAppsFeatureFlag) || ghcontext.IsInsidersMode(ctx) | |
| // or if insiders mode is active (insiders expands InsidersFeatureFlags), | |
| // but only when UI assets are available to serve the advertised resources. | |
| headerFeatures := ghcontext.GetHeaderFeatures(ctx) | |
| mcpApps := (slices.Contains(headerFeatures, github.MCPAppsFeatureFlag) || ghcontext.IsInsidersMode(ctx)) && github.UIAssetsAvailable() |
Summary
This pull request transitions MCP Apps from an insiders-only experimental feature to a general feature flag (
remote_mcp_ui_apps) that can be enabled independently.Why
Closes https://github.com/github/copilot-mcp-core/issues/1471
What changed
Documentation updates:
Feature flag implementation:
MCPAppsFeatureFlagandInsidersFeatureFlagsinfeature_flags.goto define and manage feature flags. Insiders mode now simply expands to a set of feature flags, including MCP Apps.HTTP and context handling:
WithMCPAppsinstead ofWithInsidersModeto control UI metadata stripping. [1] [2]Tool and UI logic:
issue_writeandcreate_pull_requestnow checks the MCP Apps feature flag, not insiders mode. [1] [2]Test updates:
Most important changes:
Documentation and User Guidance
remote_mcp_ui_apps), not an insiders-only feature, with updated configuration instructions and examples. [1] [2] [3] [4]Feature Flag Refactor
MCPAppsFeatureFlagandInsidersFeatureFlagsconstants; insiders mode now expands feature flags instead of controlling features directly.HTTP and Context Handling
Tool Logic
issue_writeandcreate_pull_requestis now controlled by the MCP Apps feature flag. [1] [2]MCP impact
Prompts tested (tool changes only)
Security / limits
Tool renaming
deprecated_tool_aliases.goNote: if you're renaming tools, you must add the tool aliases. For more information on how to do so, please refer to the official docs.
Lint & tests
./script/lint./script/testDocs