1f7cd9cc2f
Closes #1454. Harvested from PR #1456 by @Oliver-ZPLiu. Adds `pub headers: HashMap<String, String>` to McpServerConfig, threaded through HttpTransport::new and StreamableHttpTransport so every outbound POST applies the user-configured headers after the fixed Accept / Content-Type framing. Mirrors the field shape that Claude Code, Codex, and OpenCode already accept in their MCP config formats — unblocks Hugging Face MCP, GitHub MCP, Atlassian Rovo MCP, and any other Streamable HTTP gateway that needs a Bearer token or API key. Defense-in-depth filter (`is_safe_custom_header`) drops: * empty / whitespace-only keys (would surface as a reqwest builder error mid-send and abort the connection); * `Accept` / `Content-Type` duplicates (the MCP Streamable HTTP protocol negotiates on these exact values; a stray override would silently break tool discovery); * values containing ASCII CR or LF (response-splitting defense against a misbehaving proxy). Skipped headers emit a `tracing::warn!` and the rest of the request still goes out, so a single bad entry can't take down a server. Scope-limited vs PR #1456: * SSE legacy transport intentionally not threaded (follow-up). The PR didn't cover it; modern MCP servers are Streamable HTTP. * No env-var interpolation in v0.8.31 — headers are sent literally, matching the PR. Documented in the field doc so users know tokens pasted directly into mcp.json live there as plain text. `${VAR}` substitution is a follow-up. 8 new tests (the original PR shipped without any): config round-trip with custom headers, empty headers omitted from serialized output, accept-normal-auth, reject empty key, reject CR/LF in value, reject Accept/Content-Type override (case- insensitive), and StreamableHttpTransport stores headers. Empty-headers MCP fixtures updated at 10 sites across mcp.rs and main.rs to match the new struct shape.