|
| 1 | +# ADR-28114: Decompose MCP Setup Generator into Single-Responsibility Helpers |
| 2 | + |
| 3 | +**Date**: 2026-04-23 |
| 4 | +**Status**: Draft |
| 5 | +**Deciders**: pelikhan |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Part 1 — Narrative (Human-Friendly) |
| 10 | + |
| 11 | +### Context |
| 12 | + |
| 13 | +`pkg/workflow/mcp_setup_generator.go` contains a single orchestration function, `generateMCPSetup`, that had grown to approximately 852 lines. It handled MCP tool discovery, conditional safe-outputs config generation, agentic-workflows install step emission, safe-outputs setup, mcp-scripts setup, and MCP gateway step/env/export/container-command assembly — all inlined within one function body. The Architecture Guardian flagged this as a critical function-size violation. The function's size made individual concerns hard to navigate, test in isolation, or extend without risk of unintended side effects on adjacent logic. |
| 14 | + |
| 15 | +### Decision |
| 16 | + |
| 17 | +We will decompose `generateMCPSetup` into a thin coordinator that delegates each distinct concern to a focused helper function. `generateMCPSetup` itself becomes a sequencing orchestrator that calls `collectMCPTools`, `generateSafeOutputsConfigIfEnabled`, `generateAgenticWorkflowsInstallStep`, `generateSafeOutputsSetup`, `generateMCPScriptsSetup`, and `generateMCPGatewaySetup`. All helpers remain in `mcp_setup_generator.go`; no new packages are introduced. External behavior and output structure are preserved identically. |
| 18 | + |
| 19 | +### Alternatives Considered |
| 20 | + |
| 21 | +#### Alternative 1: Retain the Monolithic Function with Inline Comments |
| 22 | + |
| 23 | +Add section comments and godoc cross-references to document the structure of `generateMCPSetup` without splitting it. This approach has zero structural diff risk. It was rejected because documentation describes structure but does not enforce it — the function remains a single unit that cannot be tested or reviewed by concern, and the comments will drift as the function evolves. The root problem is co-mingled responsibilities, which only decomposition fixes. |
| 24 | + |
| 25 | +#### Alternative 2: Extract MCP Setup Concerns into Separate Files or a Sub-Package |
| 26 | + |
| 27 | +Move each concern into a dedicated file (e.g., `mcp_tool_collector.go`, `mcp_gateway_setup.go`) or a new `pkg/workflow/mcpsetup` sub-package. This maximises navigability at the directory level. It was not chosen because the helpers are small and tightly coupled to `WorkflowData` and `Compiler` types that live in `pkg/workflow`. A sub-package would add import indirection without meaningful boundary enforcement — all code changes together in the same PR. File-level decomposition within `mcp_setup_generator.go` is sufficient to resolve the function-size violation while keeping locality of reference. |
| 28 | + |
| 29 | +### Consequences |
| 30 | + |
| 31 | +#### Positive |
| 32 | +- `generateMCPSetup` is now a readable four-line coordinator; the intent of the setup sequence is visible at a glance. |
| 33 | +- Each helper (`collectMCPTools`, `generateMCPGatewaySetup`, etc.) can be reviewed, tested, and extended independently. |
| 34 | +- Higher-context error wrapping at orchestration boundaries (`"safe outputs setup preparation failed: %w"`) improves diagnostic clarity when errors surface. |
| 35 | +- The deduplication set for forwarded env vars and the gateway command construction are now isolated, reducing the risk of accidental mutation when either concern changes. |
| 36 | + |
| 37 | +#### Negative |
| 38 | +- The refactor produces a large diff (470 additions, 628 deletions across a single file) with no functional change, which increases reviewer burden to verify behavioral equivalence. |
| 39 | +- Adding function call boundaries introduces a small indirection cost: understanding the full setup sequence now requires reading multiple function signatures rather than one linear flow. |
| 40 | + |
| 41 | +#### Neutral |
| 42 | +- All helpers are package-private (`func`, not exported), so no public API surface changes. |
| 43 | +- The `hasGhAwSharedImport` helper extracted here may be reused by other generators in `pkg/workflow/` as a shared import-detection utility. |
| 44 | +- Existing tests continue to exercise the behavior through `generateMCPSetup`; unit tests targeting individual helpers can be added incrementally. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## Part 2 — Normative Specification (RFC 2119) |
| 49 | + |
| 50 | +> The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this section are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). |
| 51 | +
|
| 52 | +### Function Decomposition |
| 53 | + |
| 54 | +1. `generateMCPSetup` **MUST** act as a coordinator only: it **MUST NOT** contain inline logic for tool discovery, config generation, or YAML emission beyond delegating to named helpers. |
| 55 | +2. Each concern in the MCP setup pipeline (tool collection, safe-outputs config, install step emission, gateway assembly) **MUST** be implemented in a dedicated helper function with a name that describes its single responsibility. |
| 56 | +3. New logic added to the MCP setup pipeline **MUST** be placed in an appropriately scoped helper rather than inlined into `generateMCPSetup`. |
| 57 | + |
| 58 | +### File Ownership |
| 59 | + |
| 60 | +1. All MCP setup helpers **MUST** reside in `pkg/workflow/mcp_setup_generator.go` unless a helper is broadly reusable across the `pkg/workflow` package, in which case it **SHOULD** be moved to a shared file within the same package. |
| 61 | +2. MCP setup helpers **MUST NOT** be placed in a separate sub-package unless the sub-package is justified by an independent ADR. |
| 62 | + |
| 63 | +### Error Handling |
| 64 | + |
| 65 | +1. Errors returned from helpers called within `generateMCPSetup` **MUST** be wrapped with a context phrase that identifies the failing phase (e.g., `"safe outputs setup preparation failed: %w"`). |
| 66 | +2. Helper functions **SHOULD NOT** wrap errors internally when the caller can provide richer context. |
| 67 | + |
| 68 | +### Conformance |
| 69 | + |
| 70 | +An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. |
| 71 | + |
| 72 | +--- |
| 73 | + |
| 74 | +*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/24844753173) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* |
0 commit comments