You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# ADR-38945: Type GitHub MCP Mode as an Enum and Narrow Tool Config to `map[string]any`
2
+
3
+
**Date**: 2026-06-12
4
+
**Status**: Draft
5
+
6
+
## Context
7
+
8
+
The GitHub MCP configuration code in `pkg/workflow/mcp_github_config.go` had grown around two weakly-typed conventions. First, roughly 19 helper functions accepted the GitHub tool config as `githubTool any` and each immediately re-asserted it to `map[string]any` internally, scattering the same `.(map[string]any)` type assertion (and its failure handling) across nearly every function and its callers. Second, the MCP "mode" of the GitHub server (`local`, `remote`, `gh-proxy`, `cli`) was modelled as a bare `string`, so the compiler relied on string literals like `== "remote"` and `!= "gh-proxy"` with no compile-time protection against typos or invalid values. Both patterns made the code harder to read and gave the type system no leverage to catch mistakes in a security-sensitive area (proxy policy, lockdown, token selection).
9
+
10
+
## Decision
11
+
12
+
We will introduce a named `GitHubMCPMode` string type in `tools_types.go` with constants `GitHubMCPModeLocal`, `GitHubMCPModeRemote`, `GitHubMCPModeGHProxy`, and `GitHubMCPModeCLI`, change `GitHubToolConfig.Mode` from `string` to `GitHubMCPMode`, and cast the parsed value once at the single ingestion point in `tools_parser.go`. We will also narrow every `githubTool any` parameter across `mcp_github_config.go` and its callers to `map[string]any`, performing the single `.(map[string]any)` assertion once at each call site (when reading `tools["github"]`) rather than inside every helper. `normalizeGitHubType`/`getGitHubType` return `GitHubMCPMode`, and all bare string comparisons are replaced with the typed constants. We chose this because pushing the type assertion to the boundary and naming the mode values makes the security-relevant configuration paths self-documenting and compiler-checked, with no behavioral change (nil maps remain safe since Go returns zero values on reads).
13
+
14
+
## Alternatives Considered
15
+
16
+
### Alternative 1: Keep `githubTool any` and the bare-string mode
17
+
Leave the existing signatures unchanged and continue asserting to `map[string]any` inside each helper. Rejected because it perpetuates ~19 duplicated assertions and offers no compile-time defense for mode values; the duplication and stringly-typed comparisons are exactly the maintenance and correctness hazard this PR exists to remove.
18
+
19
+
### Alternative 2: Model the tool config as a concrete struct end-to-end
20
+
Replace the `map[string]any` representation entirely with a fully-typed `GitHubToolConfig` struct threaded through all helpers. Rejected (for now) as too large a change: the tool config is parsed from open-ended frontmatter and shared with generic MCP rendering paths that operate on `map[string]any`, so a full struct migration would ripple far beyond this PR. Narrowing to `map[string]any` plus a typed mode enum captures most of the type-safety benefit at a fraction of the blast radius.
21
+
22
+
## Consequences
23
+
24
+
### Positive
25
+
- The single `.(map[string]any)` assertion moves to each call site, eliminating ~19 duplicated assertions and their inconsistent failure handling inside helpers.
26
+
-`GitHubMCPMode` constants give the compiler authority over mode values, preventing typo'd or invalid mode strings in security-sensitive logic (proxy, lockdown, token selection).
27
+
- Helper signatures (`map[string]any`, `GitHubMCPMode`) now document intent directly, improving readability for future contributors.
28
+
29
+
### Negative
30
+
- Wide, mechanical change surface: ~30 non-test files plus 11 test files touched, increasing review effort and rebase/merge-conflict risk against concurrent work in `pkg/workflow`.
31
+
- Test inputs that previously exercised non-map values (e.g. `"invalid"`, `false`, `"not a map"`) are replaced with `nil`/empty maps, so the "wrong type passed in" branch is no longer covered at the helper level — that contract now lives at the call-site assertion.
32
+
- The type assertion at each call site silently yields a `nil` map on mismatch (`, _`), so a malformed `tools["github"]` value degrades to zero-value behavior rather than an explicit error.
33
+
34
+
### Neutral
35
+
-`getGitHubReadOnly()` loses its unused parameter and now takes no arguments, since it unconditionally returns `true`.
36
+
- The change is intended to be behavior-preserving; the GitHub MCP server remains unconditionally read-only and nil-map reads continue to return zero values.
37
+
38
+
---
39
+
40
+
*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/27446718826) workflow. The PR author must review, complete, and finalize this document before the PR can merge.*
0 commit comments