| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b8bfb49 commit a217a7f
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -396,6 +396,17 @@ See [Insiders Features](./insiders-features.md) for a full list of what's availa | |||
| 396 | 396 | ||
| 397 | 397 | MCP Apps is enabled by [Insiders Mode](#insiders-mode), or independently via the `remote_mcp_ui_apps` feature flag. | |
| 398 | 398 | ||
| 399 | + To keep MCP App result views enabled while making write tools execute directly | ||
| 400 | + instead of first opening an interactive form, also enable the | ||
| 401 | + `mcp_apps_disable_form_deferral` feature flag. For the remote server, send both | ||
| 402 | + flags in the request header: | ||
| 403 | + | ||
| 404 | + ```http | ||
| 405 | + X-MCP-Features: remote_mcp_ui_apps,mcp_apps_disable_form_deferral | ||
| 406 | + ``` | ||
| 407 | + | ||
| 408 | + For the local server, pass both flags to `--features`. | ||
| 409 | + | ||
| 399 | 410 | **Supported tools:** | |
| 400 | 411 | ||
| 401 | 412 | | Tool | Description | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,10 @@ import "slices" | |||
| 5 | 5 | // MCPAppsFeatureFlag is the feature flag name for MCP Apps (interactive UI forms). | |
| 6 | 6 | const MCPAppsFeatureFlag = "remote_mcp_ui_apps" | |
| 7 | 7 | ||
| 8 | + // MCPAppsDisableFormDeferralFeatureFlag disables handing write-tool calls off | ||
| 9 | + // to MCP App forms while preserving MCP Apps UI metadata and result views. | ||
| 10 | + const MCPAppsDisableFormDeferralFeatureFlag = "mcp_apps_disable_form_deferral" | ||
| 11 | + | ||
| 8 | 12 | // FeatureFlagCSVOutput is the feature flag name for CSV output on list tools. | |
| 9 | 13 | const FeatureFlagCSVOutput = "csv_output" | |
| 10 | 14 | ||
@@ -37,6 +41,7 @@ const FeatureFlagFieldsParam = "fields_param" | |||
| 37 | 41 | // This is the single source of truth for which flags are user-controllable. | |
| 38 | 42 | var AllowedFeatureFlags = []string{ | |
| 39 | 43 | MCPAppsFeatureFlag, | |
| 44 | + MCPAppsDisableFormDeferralFeatureFlag, | ||
| 40 | 45 | FeatureFlagCSVOutput, | |
| 41 | 46 | FeatureFlagIFCLabels, | |
| 42 | 47 | FeatureFlagIssuesGranular, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -155,6 +155,11 @@ func TestResolveFeatureFlags(t *testing.T) { | |||
| 155 | 155 | enabledFeatures: []string{MCPAppsFeatureFlag}, | |
| 156 | 156 | expectedFlags: []string{MCPAppsFeatureFlag}, | |
| 157 | 157 | }, | |
| 158 | + { | ||
| 159 | + name: "MCP Apps form deferral can be disabled directly", | ||
| 160 | + enabledFeatures: []string{MCPAppsDisableFormDeferralFeatureFlag}, | ||
| 161 | + expectedFlags: []string{MCPAppsDisableFormDeferralFeatureFlag}, | ||
| 162 | + }, | ||
| 158 | 163 | { | |
| 159 | 164 | name: "fields param is not enabled by default", | |
| 160 | 165 | enabledFeatures: nil, | |
@@ -183,6 +188,12 @@ func TestResolveFeatureFlags(t *testing.T) { | |||
| 183 | 188 | insidersMode: true, | |
| 184 | 189 | unexpectedFlags: []string{FeatureFlagIFCLabels}, | |
| 185 | 190 | }, | |
| 191 | + { | ||
| 192 | + name: "insiders mode does not disable MCP Apps form deferral", | ||
| 193 | + enabledFeatures: nil, | ||
| 194 | + insidersMode: true, | ||
| 195 | + unexpectedFlags: []string{MCPAppsDisableFormDeferralFeatureFlag}, | ||
| 196 | + }, | ||
| 186 | 197 | { | |
| 187 | 198 | name: "ifc_labels can be directly enabled", | |
| 188 | 199 | enabledFeatures: []string{FeatureFlagIFCLabels}, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,13 +63,15 @@ func hasNonFormParams(args map[string]any, formParams map[string]struct{}) bool | |||
| 63 | 63 | // shared by the form-backed write tools (create_pull_request, | |
| 64 | 64 | // update_pull_request, issue_write). It reports whether a call should be handed | |
| 65 | 65 | // off to its MCP App form instead of executing now: defer only when MCP Apps | |
| 66 | - // are enabled, the client can render UI, the call is not itself a form | ||
| 67 | - // submission, and every supplied parameter can be represented by the form | ||
| 68 | - // (formParams is the tool's form-parameter allowlist). When it returns false | ||
| 69 | - // the handler executes directly; the host may still render the tool's view, | ||
| 70 | - // which renders the result rather than an input form. | ||
| 66 | + // are enabled, form deferral has not been disabled, the client can render UI, | ||
| 67 | + // the call is not itself a form submission, and every supplied parameter can | ||
| 68 | + // be represented by the form (formParams is the tool's form-parameter | ||
| 69 | + // allowlist). When it returns false the handler executes directly; the host may | ||
| 70 | + // still render the tool's view, which renders the result rather than an input | ||
| 71 | + // form. | ||
| 71 | 72 | func shouldDeferToForm(ctx context.Context, deps ToolDependencies, req *mcp.CallToolRequest, args map[string]any, formParams map[string]struct{}) bool { | |
| 72 | 73 | return deps.IsFeatureEnabled(ctx, MCPAppsFeatureFlag) && | |
| 74 | + !deps.IsFeatureEnabled(ctx, MCPAppsDisableFormDeferralFeatureFlag) && | ||
| 73 | 75 | clientSupportsUI(ctx, req) && | |
| 74 | 76 | !uiSubmitted(args) && | |
| 75 | 77 | !hasNonFormParams(args, formParams) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -85,3 +85,48 @@ func Test_clientSupportsUI_fromContext(t *testing.T) { | |||
| 85 | 85 | assert.False(t, clientSupportsUI(context.Background(), nil)) | |
| 86 | 86 | }) | |
| 87 | 87 | } | |
| 88 | + | ||
| 89 | + func Test_shouldDeferToForm_featureFlags(t *testing.T) { | ||
| 90 | + t.Parallel() | ||
| 91 | + | ||
| 92 | + ctx := ghcontext.WithUISupport(context.Background(), true) | ||
| 93 | + args := map[string]any{"owner": "octocat"} | ||
| 94 | + formParams := map[string]struct{}{"owner": {}} | ||
| 95 | + | ||
| 96 | + tests := []struct { | ||
| 97 | + name string | ||
| 98 | + enabledFlags []string | ||
| 99 | + want bool | ||
| 100 | + }{ | ||
| 101 | + { | ||
| 102 | + name: "MCP Apps enabled defers to form", | ||
| 103 | + enabledFlags: []string{MCPAppsFeatureFlag}, | ||
| 104 | + want: true, | ||
| 105 | + }, | ||
| 106 | + { | ||
| 107 | + name: "form deferral disabled executes directly", | ||
| 108 | + enabledFlags: []string{ | ||
| 109 | + MCPAppsFeatureFlag, | ||
| 110 | + MCPAppsDisableFormDeferralFeatureFlag, | ||
| 111 | + }, | ||
| 112 | + want: false, | ||
| 113 | + }, | ||
| 114 | + { | ||
| 115 | + name: "form deferral opt-out does not enable MCP Apps", | ||
| 116 | + enabledFlags: []string{MCPAppsDisableFormDeferralFeatureFlag}, | ||
| 117 | + want: false, | ||
| 118 | + }, | ||
| 119 | + { | ||
| 120 | + name: "MCP Apps disabled executes directly", | ||
| 121 | + want: false, | ||
| 122 | + }, | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + for _, tc := range tests { | ||
| 126 | + t.Run(tc.name, func(t *testing.T) { | ||
| 127 | + t.Parallel() | ||
| 128 | + deps := BaseDeps{featureChecker: featureCheckerFor(tc.enabledFlags...)} | ||
| 129 | + assert.Equal(t, tc.want, shouldDeferToForm(ctx, deps, nil, args, formParams)) | ||
| 130 | + }) | ||
| 131 | + } | ||
| 132 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,6 +38,12 @@ func TestCreateHTTPFeatureChecker(t *testing.T) { | |||
| 38 | 38 | headerFeatures: []string{github.MCPAppsFeatureFlag}, | |
| 39 | 39 | wantEnabled: true, | |
| 40 | 40 | }, | |
| 41 | + { | ||
| 42 | + name: "MCP Apps form deferral opt-out accepted from header", | ||
| 43 | + flagName: github.MCPAppsDisableFormDeferralFeatureFlag, | ||
| 44 | + headerFeatures: []string{github.MCPAppsDisableFormDeferralFeatureFlag}, | ||
| 45 | + wantEnabled: true, | ||
| 46 | + }, | ||
| 41 | 47 | { | |
| 42 | 48 | name: "unknown flag in header is ignored", | |
| 43 | 49 | flagName: "unknown_flag", | |
@@ -74,6 +80,12 @@ func TestCreateHTTPFeatureChecker(t *testing.T) { | |||
| 74 | 80 | insidersMode: true, | |
| 75 | 81 | wantEnabled: true, | |
| 76 | 82 | }, | |
| 83 | + { | ||
| 84 | + name: "insiders mode does not disable MCP Apps form deferral", | ||
| 85 | + flagName: github.MCPAppsDisableFormDeferralFeatureFlag, | ||
| 86 | + insidersMode: true, | ||
| 87 | + wantEnabled: false, | ||
| 88 | + }, | ||
| 77 | 89 | { | |
| 78 | 90 | name: "static feature is enabled without header", | |
| 79 | 91 | staticFeatures: []string{github.FeatureFlagCSVOutput}, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments