Problem
crates/tui/src/mcp.rs bundled every MCP concern in one file. The transport half of the split has since landed: StdioTransport, SseTransport, StreamableHttpTransport, header helpers, OAuth, and the test module all live under crates/tui/src/mcp/ now. The root file is still 3,189 lines because the HTTP transport, connection lifecycle, pool, config load/save, and discovery-snapshot layers never moved. The remaining boundaries are just as natural as the ones already cut; finishing the split keeps each concern reviewable and stops unrelated MCP work from colliding in one file.
Current evidence
Measured at v0.9.1 (2026-07-18):
- Already extracted:
crates/tui/src/mcp/stdio.rs (313 lines), mcp/sse.rs (346), mcp/streamable_http.rs (209), mcp/headers.rs (65), mcp/oauth.rs (1,179), mcp/tests.rs (3,887 lines, 102 tests). Transports implement the shared McpTransport trait and import root helpers via use super::{...}.
- Root
crates/tui/src/mcp.rs (3,189 lines) still holds:
- Config-path validation and env expansion:
validate_mcp_config_path (42), expand_env_placeholders (63), mcp_stdio_child_env (110).
- Command availability probing:
McpCommandAvailability (118), static_mcp_command_availability (238).
- Secret redaction (security-sensitive):
mask_url_secrets (286), redact_proxy_userinfo (305), redact_body_preview (334), bounded_body_excerpt (360), invalid_json_preview (375).
- Config types:
McpConfig (395), McpTimeouts (405), McpServerConfig (436), McpServerOAuthConfig (522).
- Discovery item types:
McpTool (566), McpResource (603), McpResourceTemplate (614), McpPrompt (626).
ConnectionState (648), McpServerCapabilities (655), response_result (672), run_optional_discovery (682).
McpTransport trait (715) — the common abstraction already exists.
- The last unextracted transport:
HttpTransport + McpHttpAuth (726–939, impl McpTransport at 895).
- SSE wire-format parsing shared by three transports:
parse_sse_message_data (963), find_sse_event_separator[_bytes] (994/1008), sse_field_value (1029); stale-session detection (940–962); validate_mcp_transport (1042).
- Connection lifecycle:
McpConnection (1067–1761) and McpPool (1763–2540).
- Snapshot types (2541–2578) and config load/save/init:
load_config (2579), workspace_mcp_config_path (2629), load_config_with_workspace (2635), plugin merge (2648–2712), workspace path safety (checked_workspace_mcp_config_path 2717, checked_workspace_path 2723, normalize_path_components 2783), save_config (2840), init_config (2908), add_server_config (2929), remove_server_config (2970), set_server_enabled (2978).
- Discovery snapshot:
manager_snapshot_from_config[_with_workspace] (2990/3004), discover_manager_snapshot[_with_workspace] (3020/3045), snapshot_from_config (3071).
mod tests; (3189).
- Consumers span 14 files including
crates/tui/src/main.rs, crates/tui/src/tools/registry.rs, crates/tui/src/tools/runtime_mcp.rs, crates/tui/src/core/engine.rs, crates/tui/src/runtime_api.rs, and the TUI setup/MCP routing surfaces — so root re-exports must keep crate::mcp::X paths stable.
Scope
One transport/concern per PR, smallest first:
mcp/http.rs — move HttpTransport, HttpTransportMode, McpHttpAuth, and mcp_headers_have_authorization (726–939). After this every transport lives in its own module.
mcp/wire.rs (or fold into an existing helper module) — the shared SSE wire parsing + stale-session/connection-closed detection (940–1066), imported by sse.rs, streamable_http.rs, and http.rs.
mcp/connection.rs — ConnectionState, McpServerCapabilities, response_result, run_optional_discovery, response_id_matches, McpConnection + Drop (1067–1761).
mcp/pool.rs — McpPool (1763–2540).
mcp/config.rs — config types (395–565), env expansion + config-path validation (42–116), load/save/init/add/remove/enable (2579–2989) including the workspace path-safety helpers, which must move verbatim.
mcp/snapshot.rs — snapshot types (2541–2578) plus manager_snapshot_* / discover_manager_snapshot_* / snapshot_from_config (2990–3188).
- Keep in the root:
McpTransport trait, the redaction helpers (or give them a tiny mcp/redact.rs), command-availability probing, and re-exports preserving today's public paths.
Key files
crates/tui/src/mcp.rs
crates/tui/src/mcp/tests.rs
crates/tui/src/mcp/stdio.rs
crates/tui/src/mcp/sse.rs
crates/tui/src/mcp/streamable_http.rs
crates/tui/src/mcp/headers.rs
Acceptance criteria
Verification
cargo test -p codewhale-tui --bin codewhale-tui --locked mcp
cargo fmt
Include one config load/save/discovery smoke in the touched-test run (already covered inside mcp/tests.rs).
Out of scope
- Any behavior change to transports, auth, retry/stale-session handling, or discovery.
crates/tui/src/mcp_server.rs (the server side) and tools/runtime_mcp.rs beyond import updates.
- OAuth flows (
mcp/oauth.rs is already separate).
Related
Triage note: body restructured for agent execution on 2026-07-18; prior comment refinements folded in. Original wording preserved in edit history.
Problem
crates/tui/src/mcp.rsbundled every MCP concern in one file. The transport half of the split has since landed:StdioTransport,SseTransport,StreamableHttpTransport, header helpers, OAuth, and the test module all live undercrates/tui/src/mcp/now. The root file is still 3,189 lines because the HTTP transport, connection lifecycle, pool, config load/save, and discovery-snapshot layers never moved. The remaining boundaries are just as natural as the ones already cut; finishing the split keeps each concern reviewable and stops unrelated MCP work from colliding in one file.Current evidence
Measured at v0.9.1 (2026-07-18):
crates/tui/src/mcp/stdio.rs(313 lines),mcp/sse.rs(346),mcp/streamable_http.rs(209),mcp/headers.rs(65),mcp/oauth.rs(1,179),mcp/tests.rs(3,887 lines, 102 tests). Transports implement the sharedMcpTransporttrait and import root helpers viause super::{...}.crates/tui/src/mcp.rs(3,189 lines) still holds:validate_mcp_config_path(42),expand_env_placeholders(63),mcp_stdio_child_env(110).McpCommandAvailability(118),static_mcp_command_availability(238).mask_url_secrets(286),redact_proxy_userinfo(305),redact_body_preview(334),bounded_body_excerpt(360),invalid_json_preview(375).McpConfig(395),McpTimeouts(405),McpServerConfig(436),McpServerOAuthConfig(522).McpTool(566),McpResource(603),McpResourceTemplate(614),McpPrompt(626).ConnectionState(648),McpServerCapabilities(655),response_result(672),run_optional_discovery(682).McpTransporttrait (715) — the common abstraction already exists.HttpTransport+McpHttpAuth(726–939,impl McpTransportat 895).parse_sse_message_data(963),find_sse_event_separator[_bytes](994/1008),sse_field_value(1029); stale-session detection (940–962);validate_mcp_transport(1042).McpConnection(1067–1761) andMcpPool(1763–2540).load_config(2579),workspace_mcp_config_path(2629),load_config_with_workspace(2635), plugin merge (2648–2712), workspace path safety (checked_workspace_mcp_config_path2717,checked_workspace_path2723,normalize_path_components2783),save_config(2840),init_config(2908),add_server_config(2929),remove_server_config(2970),set_server_enabled(2978).manager_snapshot_from_config[_with_workspace](2990/3004),discover_manager_snapshot[_with_workspace](3020/3045),snapshot_from_config(3071).mod tests;(3189).crates/tui/src/main.rs,crates/tui/src/tools/registry.rs,crates/tui/src/tools/runtime_mcp.rs,crates/tui/src/core/engine.rs,crates/tui/src/runtime_api.rs, and the TUI setup/MCP routing surfaces — so root re-exports must keepcrate::mcp::Xpaths stable.Scope
One transport/concern per PR, smallest first:
mcp/http.rs— moveHttpTransport,HttpTransportMode,McpHttpAuth, andmcp_headers_have_authorization(726–939). After this every transport lives in its own module.mcp/wire.rs(or fold into an existing helper module) — the shared SSE wire parsing + stale-session/connection-closed detection (940–1066), imported bysse.rs,streamable_http.rs, andhttp.rs.mcp/connection.rs—ConnectionState,McpServerCapabilities,response_result,run_optional_discovery,response_id_matches,McpConnection+Drop(1067–1761).mcp/pool.rs—McpPool(1763–2540).mcp/config.rs— config types (395–565), env expansion + config-path validation (42–116), load/save/init/add/remove/enable (2579–2989) including the workspace path-safety helpers, which must move verbatim.mcp/snapshot.rs— snapshot types (2541–2578) plusmanager_snapshot_*/discover_manager_snapshot_*/snapshot_from_config(2990–3188).McpTransporttrait, the redaction helpers (or give them a tinymcp/redact.rs), command-availability probing, and re-exports preserving today's public paths.Key files
crates/tui/src/mcp.rscrates/tui/src/mcp/tests.rscrates/tui/src/mcp/stdio.rscrates/tui/src/mcp/sse.rscrates/tui/src/mcp/streamable_http.rscrates/tui/src/mcp/headers.rsAcceptance criteria
McpTransportand shared types stay in a small common surface.McpConnection,McpPool) is separated from individual transports and from config I/O.checked_workspace_*) and secret-redaction helpers move whole with behavior unchanged.mcp/tests.rspass unmodified; consumers keep compiling withcrate::mcp::paths (root re-exports, no broadpubsweep — extracted transports today arepub(super), keep that bar).Verification
Include one config load/save/discovery smoke in the touched-test run (already covered inside
mcp/tests.rs).Out of scope
crates/tui/src/mcp_server.rs(the server side) andtools/runtime_mcp.rsbeyond import updates.mcp/oauth.rsis already separate).Related
Triage note: body restructured for agent execution on 2026-07-18; prior comment refinements folded in. Original wording preserved in edit history.