diff --git a/docs/adr/54714-share-github-mcp-common-options-via-embedded-struct.md b/docs/adr/54714-share-github-mcp-common-options-via-embedded-struct.md new file mode 100644 index 00000000000..246b7da9339 --- /dev/null +++ b/docs/adr/54714-share-github-mcp-common-options-via-embedded-struct.md @@ -0,0 +1,44 @@ +# ADR-54714: Share GitHub MCP Common Options via Embedded Struct + +**Date**: 2026-08-22 +**Status**: Draft +**Deciders**: pelikhan, copilot-swe-agent + +--- + +### Context + +The GitHub MCP renderer supports two transport modes — Docker (local) and Remote (hosted). Each mode has its own configuration struct (`GitHubMCPDockerOptions` and `GitHubMCPRemoteOptions`). Both structs independently declared the same 8 fields: `ReadOnly`, `Lockdown`, `LockdownFromStep`, `GuardPoliciesFromStep`, `Toolsets`, `Features`, `AllowedTools`, and `GuardPolicies`. These fields control shared MCP behaviour (read-only access, lockdown enforcement, guard policies, toolset selection, feature flags, and allowed-tool filtering) regardless of transport. The duplication meant that any change to shared behaviour required coordinated edits in two places, with no compiler-level guarantee that the structs remained in sync, creating ongoing drift risk. + +### Decision + +We will introduce `GitHubMCPCommonOptions` as a new shared struct containing all 8 transport-agnostic fields, and update `GitHubMCPDockerOptions` and `GitHubMCPRemoteOptions` to embed it anonymously. A regression test (`TestGitHubMCPOptionsEmbedCommonOptions`) uses reflection to assert that both transport structs embed `GitHubMCPCommonOptions`, enforcing the constraint at compile/test time. All construction sites in `mcp_renderer_github.go` initialise shared fields via the embedded struct literal. + +### Alternatives Considered + +#### Alternative 1: Keep duplicated fields (status quo) + +Each transport struct retains its own independent copy of the 8 shared fields. Behaviour is identical to the new approach at runtime. Rejected because: there is no mechanism to prevent the structs from diverging independently — the problem that motivated this PR — and future changes to shared fields must always be made twice with no compiler enforcement. + +#### Alternative 2: Shared builder function instead of struct embedding + +A helper function (e.g., `newCommonOptions(...)`) could accept the common arguments and populate each transport struct's fields individually. This avoids anonymous embedding and keeps field access flat. Rejected because: it does not create a named type boundary visible in struct literals, making it harder to see at a glance which fields are shared; and it provides no compile-time or test-time guarantee that both transport structs expose the same set of shared fields. + +### Consequences + +#### Positive +- Single definition for all shared GitHub MCP configuration fields; any new shared field is added once. +- Compile/test-time enforcement via `TestGitHubMCPOptionsEmbedCommonOptions` prevents future structs from silently omitting the embedded type. +- Reduced field count in transport-specific structs; transport-specific fields are clearly distinguished from shared ones. + +#### Negative +- Call sites must use the explicit embedded struct key (`GitHubMCPCommonOptions: GitHubMCPCommonOptions{...}`) in keyed struct literals, which is more verbose than flat field assignment. +- Go's field promotion means shared fields appear on the transport struct's surface, which can obscure their origin for readers unfamiliar with the embedding relationship. + +#### Neutral +- All existing test cases required mechanical updates to use the embedded struct literal syntax — no test logic changed, only struct initialisation syntax. +- The regression test uses `reflect.Type.FieldByName` + `Anonymous` check, which is a non-zero dependency on reflection in the test suite. + +--- + +*ADR created by [adr-writer agent]. Review and finalize before changing status from Draft to Accepted.* diff --git a/pkg/workflow/engine_helpers_github_test.go b/pkg/workflow/engine_helpers_github_test.go index cbeb2bae49e..e6ca95ae59b 100644 --- a/pkg/workflow/engine_helpers_github_test.go +++ b/pkg/workflow/engine_helpers_github_test.go @@ -17,12 +17,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Claude engine configuration (no type field, with effective token)", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "latest", CustomArgs: nil, IncludeTypeField: false, - AllowedTools: nil, EffectiveToken: "${{ secrets.GITHUB_TOKEN }}", }, expected: []string{ @@ -42,12 +44,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Copilot engine configuration (with type field, no effective token)", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: []string{"create_issue", "issue_read"}, + }, DockerImageVersion: "latest", CustomArgs: nil, IncludeTypeField: true, - AllowedTools: []string{"create_issue", "issue_read"}, EffectiveToken: "", }, expected: []string{ @@ -67,12 +71,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Read-only mode enabled", options: GitHubMCPDockerOptions{ - ReadOnly: true, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "v1.0.0", CustomArgs: nil, IncludeTypeField: false, - AllowedTools: nil, EffectiveToken: "", }, expected: []string{ @@ -90,12 +96,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Custom args provided", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "latest", CustomArgs: []string{"--verbose", "--debug"}, IncludeTypeField: false, - AllowedTools: nil, EffectiveToken: "", }, expected: []string{ @@ -111,12 +119,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Copilot with wildcard tools (no allowed tools specified)", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: nil, // When nil, should default to wildcard + }, DockerImageVersion: "latest", CustomArgs: nil, IncludeTypeField: true, - AllowedTools: nil, // When nil, should default to wildcard EffectiveToken: "", }, expected: []string{ @@ -131,12 +141,14 @@ func TestRenderGitHubMCPDockerConfig(t *testing.T) { { name: "Custom toolsets", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Toolsets: "repos,issues,pull_requests", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "repos,issues,pull_requests", + AllowedTools: nil, + }, DockerImageVersion: "latest", CustomArgs: nil, IncludeTypeField: false, - AllowedTools: nil, EffectiveToken: "", }, expected: []string{ @@ -176,12 +188,14 @@ func TestRenderGitHubMCPDockerConfig_OutputStructure(t *testing.T) { // Test that the output has the expected JSON structure var yaml strings.Builder RenderGitHubMCPDockerConfig(&yaml, GitHubMCPDockerOptions{ - ReadOnly: true, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Toolsets: "default", + AllowedTools: []string{"tool1", "tool2"}, + }, DockerImageVersion: "latest", CustomArgs: []string{"--test"}, IncludeTypeField: true, - AllowedTools: []string{"tool1", "tool2"}, EffectiveToken: "", }) diff --git a/pkg/workflow/github_lockdown_test.go b/pkg/workflow/github_lockdown_test.go index 2c35fe8f246..01598f9f89e 100644 --- a/pkg/workflow/github_lockdown_test.go +++ b/pkg/workflow/github_lockdown_test.go @@ -114,12 +114,14 @@ func TestRenderGitHubMCPDockerConfigWithLockdown(t *testing.T) { { name: "Docker mode with lockdown enabled", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Lockdown: true, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Lockdown: true, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "latest", IncludeTypeField: true, - AllowedTools: nil, }, expected: []string{ `"type": "stdio"`, @@ -132,12 +134,14 @@ func TestRenderGitHubMCPDockerConfigWithLockdown(t *testing.T) { { name: "Docker mode with lockdown disabled", options: GitHubMCPDockerOptions{ - ReadOnly: false, - Lockdown: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Lockdown: false, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "latest", IncludeTypeField: true, - AllowedTools: nil, }, expected: []string{ `"type": "stdio"`, @@ -151,12 +155,14 @@ func TestRenderGitHubMCPDockerConfigWithLockdown(t *testing.T) { { name: "Docker mode with lockdown and read-only both enabled", options: GitHubMCPDockerOptions{ - ReadOnly: true, - Lockdown: true, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Lockdown: true, + Toolsets: "default", + AllowedTools: nil, + }, DockerImageVersion: "v1.0.0", IncludeTypeField: false, - AllowedTools: nil, }, expected: []string{ `"GITHUB_READ_ONLY": "1"`, @@ -200,12 +206,14 @@ func TestRenderGitHubMCPRemoteConfigWithLockdown(t *testing.T) { { name: "Remote mode with lockdown enabled", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Lockdown: true, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Lockdown: true, + Toolsets: "default", + AllowedTools: []string{"*"}, + }, AuthorizationValue: "Bearer test-token", IncludeToolsField: true, - AllowedTools: []string{"*"}, IncludeEnvSection: false, }, expected: []string{ @@ -221,12 +229,14 @@ func TestRenderGitHubMCPRemoteConfigWithLockdown(t *testing.T) { { name: "Remote mode with lockdown disabled", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Lockdown: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Lockdown: false, + Toolsets: "default", + AllowedTools: []string{"*"}, + }, AuthorizationValue: "Bearer test-token", IncludeToolsField: true, - AllowedTools: []string{"*"}, IncludeEnvSection: false, }, expected: []string{ @@ -242,12 +252,14 @@ func TestRenderGitHubMCPRemoteConfigWithLockdown(t *testing.T) { { name: "Remote mode with lockdown and read-only both enabled", options: GitHubMCPRemoteOptions{ - ReadOnly: true, - Lockdown: true, - Toolsets: "repos,issues", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Lockdown: true, + Toolsets: "repos,issues", + AllowedTools: nil, + }, AuthorizationValue: "Bearer test-token", IncludeToolsField: false, - AllowedTools: nil, IncludeEnvSection: false, }, expected: []string{ diff --git a/pkg/workflow/github_remote_config_test.go b/pkg/workflow/github_remote_config_test.go index fc396deaed3..e446248ddac 100644 --- a/pkg/workflow/github_remote_config_test.go +++ b/pkg/workflow/github_remote_config_test.go @@ -17,11 +17,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "Claude-style config without tools or env", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: nil, + }, AuthorizationValue: "Bearer ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", IncludeToolsField: false, - AllowedTools: nil, IncludeEnvSection: false, }, expectedOutput: []string{ @@ -40,11 +42,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "Claude-style config with read-only", options: GitHubMCPRemoteOptions{ - ReadOnly: true, - Toolsets: "repos,issues", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Toolsets: "repos,issues", + AllowedTools: nil, + }, AuthorizationValue: "Bearer ${{ secrets.CUSTOM_PAT }}", IncludeToolsField: false, - AllowedTools: nil, IncludeEnvSection: false, }, expectedOutput: []string{ @@ -63,11 +67,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "Copilot-style config with tools and env", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: []string{"list_issues", "create_issue"}, + }, AuthorizationValue: "Bearer \\${GITHUB_PERSONAL_ACCESS_TOKEN}", IncludeToolsField: true, - AllowedTools: []string{"list_issues", "create_issue"}, IncludeEnvSection: true, }, expectedOutput: []string{ @@ -92,11 +98,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "Copilot-style config with wildcard tools", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Toolsets: "all", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "all", + AllowedTools: nil, // Empty array should result in wildcard + }, AuthorizationValue: "Bearer \\${GITHUB_PERSONAL_ACCESS_TOKEN}", IncludeToolsField: true, - AllowedTools: nil, // Empty array should result in wildcard IncludeEnvSection: true, }, expectedOutput: []string{ @@ -117,11 +125,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "Copilot-style config with read-only and specific tools", options: GitHubMCPRemoteOptions{ - ReadOnly: true, - Toolsets: "repos", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Toolsets: "repos", + AllowedTools: []string{"list_repositories", "get_repository"}, + }, AuthorizationValue: "Bearer \\${GITHUB_PERSONAL_ACCESS_TOKEN}", IncludeToolsField: true, - AllowedTools: []string{"list_repositories", "get_repository"}, IncludeEnvSection: true, }, expectedOutput: []string{ @@ -145,11 +155,13 @@ func TestRenderGitHubMCPRemoteConfig(t *testing.T) { { name: "No toolsets configured", options: GitHubMCPRemoteOptions{ - ReadOnly: false, - Toolsets: "", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "", + AllowedTools: nil, + }, AuthorizationValue: "Bearer ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", IncludeToolsField: false, - AllowedTools: nil, IncludeEnvSection: false, }, expectedOutput: []string{ @@ -194,11 +206,13 @@ func TestRenderGitHubMCPRemoteConfigHeaderOrder(t *testing.T) { // Test that headers are sorted alphabetically for deterministic output var yaml strings.Builder RenderGitHubMCPRemoteConfig(&yaml, GitHubMCPRemoteOptions{ - ReadOnly: true, - Toolsets: "repos,issues", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: true, + Toolsets: "repos,issues", + AllowedTools: nil, + }, AuthorizationValue: "Bearer token", IncludeToolsField: false, - AllowedTools: nil, IncludeEnvSection: false, }) output := yaml.String() @@ -225,11 +239,13 @@ func TestRenderGitHubMCPRemoteConfigToolsCommas(t *testing.T) { // Test that tools array is properly formatted with commas var yaml strings.Builder RenderGitHubMCPRemoteConfig(&yaml, GitHubMCPRemoteOptions{ - ReadOnly: false, - Toolsets: "default", + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: false, + Toolsets: "default", + AllowedTools: []string{"tool1", "tool2", "tool3"}, + }, AuthorizationValue: "Bearer token", IncludeToolsField: true, - AllowedTools: []string{"tool1", "tool2", "tool3"}, IncludeEnvSection: true, }) output := yaml.String() diff --git a/pkg/workflow/mcp_renderer_github.go b/pkg/workflow/mcp_renderer_github.go index 556a048bba6..2ba310ce374 100644 --- a/pkg/workflow/mcp_renderer_github.go +++ b/pkg/workflow/mcp_renderer_github.go @@ -66,17 +66,19 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github } RenderGitHubMCPRemoteConfig(yaml, GitHubMCPRemoteOptions{ - ReadOnly: readOnly, - Lockdown: lockdown, - LockdownFromStep: false, - GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy, - Toolsets: toolsets, - Features: features, - AuthorizationValue: authValue, - IncludeToolsField: r.options.IncludeCopilotFields, - AllowedTools: getGitHubAllowedTools(githubTool), - IncludeEnvSection: r.options.IncludeCopilotFields, - GuardPolicies: explicitGuardPolicies, + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: readOnly, + Lockdown: lockdown, + LockdownFromStep: false, + GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy, + Toolsets: toolsets, + Features: features, + AllowedTools: getGitHubAllowedTools(githubTool), + GuardPolicies: explicitGuardPolicies, + }, + AuthorizationValue: authValue, + IncludeToolsField: r.options.IncludeCopilotFields, + IncludeEnvSection: r.options.IncludeCopilotFields, }) } else { // Local mode - use Docker-based GitHub MCP server (default) @@ -86,19 +88,21 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github mcpRendererLog.Printf("GitHub MCP local docker mode: image_version=%s, custom_args=%d", githubDockerImageVersion, len(customArgs)) RenderGitHubMCPDockerConfig(yaml, GitHubMCPDockerOptions{ - ReadOnly: readOnly, - Lockdown: lockdown, - LockdownFromStep: false, - GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy, - Toolsets: toolsets, - Features: features, - DockerImageVersion: githubDockerImageVersion, - CustomArgs: customArgs, - IncludeTypeField: r.options.IncludeCopilotFields, - AllowedTools: getGitHubAllowedTools(githubTool), - EffectiveToken: "", // Token passed via env - GuardPolicies: explicitGuardPolicies, - ContainerPinMappings: r.options.ContainerPinMappings, + GitHubMCPCommonOptions: GitHubMCPCommonOptions{ + ReadOnly: readOnly, + Lockdown: lockdown, + LockdownFromStep: false, + GuardPoliciesFromStep: shouldUseStepOutputForGuardPolicy, + Toolsets: toolsets, + Features: features, + AllowedTools: getGitHubAllowedTools(githubTool), + GuardPolicies: explicitGuardPolicies, + }, + DockerImageVersion: githubDockerImageVersion, + CustomArgs: customArgs, + IncludeTypeField: r.options.IncludeCopilotFields, + EffectiveToken: "", // Token passed via env + ContainerPinMappings: r.options.ContainerPinMappings, }) } diff --git a/pkg/workflow/mcp_renderer_types.go b/pkg/workflow/mcp_renderer_types.go index a56848bc8f3..1c3f8810d15 100644 --- a/pkg/workflow/mcp_renderer_types.go +++ b/pkg/workflow/mcp_renderer_types.go @@ -63,8 +63,8 @@ type JSONMCPConfigOptions struct { GatewayConfig *MCPGatewayRuntimeConfig } -// GitHubMCPDockerOptions defines configuration for GitHub MCP Docker rendering -type GitHubMCPDockerOptions struct { +// GitHubMCPCommonOptions defines shared configuration for GitHub MCP rendering. +type GitHubMCPCommonOptions struct { // ReadOnly enables read-only mode for GitHub API operations ReadOnly bool // Lockdown enables lockdown mode for GitHub MCP server (limits content from public repos) @@ -77,20 +77,25 @@ type GitHubMCPDockerOptions struct { // Toolsets specifies the GitHub toolsets to enable Toolsets string // Features is a comma-separated list of GitHub MCP feature flags to enable (e.g. "fields_param"). - // Emitted as GITHUB_FEATURES env var for the Docker container. + // Emitted as GITHUB_FEATURES env var for Docker or X-MCP-Features header for remote mode. Features string + // AllowedTools specifies the list of allowed tools (Copilot uses this, Claude doesn't) + AllowedTools []string + // GuardPolicies specifies access control policies for the MCP gateway (e.g., allow-only repos/integrity) + GuardPolicies map[string]any +} + +// GitHubMCPDockerOptions defines configuration for GitHub MCP Docker rendering +type GitHubMCPDockerOptions struct { + GitHubMCPCommonOptions // DockerImageVersion specifies the GitHub MCP server Docker image version DockerImageVersion string // CustomArgs are additional arguments to append to the Docker command CustomArgs []string // IncludeTypeField indicates whether to include the "type": "stdio" field (Copilot needs it, Claude doesn't) IncludeTypeField bool - // AllowedTools specifies the list of allowed tools (Copilot uses this, Claude doesn't) - AllowedTools []string // EffectiveToken is the GitHub token to use (Claude uses this, Copilot uses env passthrough) EffectiveToken string - // GuardPolicies specifies access control policies for the MCP gateway (e.g., allow-only repos/integrity) - GuardPolicies map[string]any // ContainerPinMappings maps source container image references to their SHA-pinned replacements. // When set, the GitHub MCP server container reference is redirected to the mapped private // registry mirror (digest stripped for MCP Gateway compatibility). Nil → no redirect. @@ -99,30 +104,13 @@ type GitHubMCPDockerOptions struct { // GitHubMCPRemoteOptions defines configuration for GitHub MCP remote mode rendering type GitHubMCPRemoteOptions struct { - // ReadOnly enables read-only mode for GitHub API operations - ReadOnly bool - // Lockdown enables lockdown mode for GitHub MCP server (limits content from public repos) - Lockdown bool - // LockdownFromStep indicates if lockdown value should be read from step output - LockdownFromStep bool - // GuardPoliciesFromStep indicates if guard policy values should be read from step outputs - // (GITHUB_MCP_GUARD_MIN_INTEGRITY and GITHUB_MCP_GUARD_REPOS env vars) - GuardPoliciesFromStep bool - // Toolsets specifies the GitHub toolsets to enable - Toolsets string - // Features is a comma-separated list of GitHub MCP feature flags to enable (e.g. "fields_param"). - // Emitted as X-MCP-Features header for the hosted endpoint. - Features string + GitHubMCPCommonOptions // AuthorizationValue is the value for the Authorization header // For Claude: "Bearer {effectiveToken}" // For Copilot: "Bearer \\${GITHUB_PERSONAL_ACCESS_TOKEN}" AuthorizationValue string // IncludeToolsField indicates whether to include the "tools" field (Copilot needs it, Claude doesn't) IncludeToolsField bool - // AllowedTools specifies the list of allowed tools (Copilot uses this, Claude doesn't) - AllowedTools []string // IncludeEnvSection indicates whether to include the env section (Copilot needs it, Claude doesn't) IncludeEnvSection bool - // GuardPolicies specifies access control policies for the MCP gateway (e.g., allow-only repos/integrity) - GuardPolicies map[string]any } diff --git a/pkg/workflow/mcp_renderer_types_test.go b/pkg/workflow/mcp_renderer_types_test.go new file mode 100644 index 00000000000..e2c3c5525fc --- /dev/null +++ b/pkg/workflow/mcp_renderer_types_test.go @@ -0,0 +1,30 @@ +//go:build !integration + +package workflow + +import ( + "reflect" + "testing" +) + +func TestGitHubMCPOptionsEmbedCommonOptions(t *testing.T) { + tests := []struct { + name string + optionType reflect.Type + }{ + {name: "docker", optionType: reflect.TypeFor[GitHubMCPDockerOptions]()}, + {name: "remote", optionType: reflect.TypeFor[GitHubMCPRemoteOptions]()}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + field, ok := tt.optionType.FieldByName("GitHubMCPCommonOptions") + if !ok { + t.Fatalf("expected %s to embed GitHubMCPCommonOptions", tt.optionType.Name()) + } + if !field.Anonymous { + t.Fatalf("expected %s.GitHubMCPCommonOptions to be embedded", tt.optionType.Name()) + } + }) + } +} diff --git a/pkg/workflow/schemas/github-workflow.json b/pkg/workflow/schemas/github-workflow.json index fd902c7129e..3a757bb4e95 100644 --- a/pkg/workflow/schemas/github-workflow.json +++ b/pkg/workflow/schemas/github-workflow.json @@ -260,9 +260,6 @@ "discussions": { "$ref": "#/definitions/permissions-level" }, - "drives": { - "$ref": "#/definitions/permissions-level" - }, "id-token": { "$ref": "#/definitions/permissions-level" },