Files
codewhale/docs/rfcs/2190-mcp-modularization.md
T
Hunter Bown 1763261503 v0.8.46: release archives, sandbox depth, quick fixes, web install, docs
* docs: v0.8.46 CHANGELOG — platform archives, palette, sub-agents, sandbox, web install, search fixes

Closes #2188

* feat(v0.8.46): quick fixes — palette, model picker Esc, sub-agent sidebar, shell chip, model name casing, CVE bump (#2212)

* fix: bump qs to >=6.15.2 for CVE-2026-8723

Add qs override in feishu-bridge package.json to force transitive
dependency resolution to >=6.15.2, addressing CVE-2026-8723.

Refs: #2198

* fix: Esc in model picker applies last-highlighted choice

Previously Esc reverted to the initial model when the user hadn't
moved the selection. Now Esc always applies the currently highlighted
model and thinking-effort tier, making Esc consistent with Enter.

Also updates the picker footer hint from 'Esc cancel' to 'Esc apply'.

Refs: #2196

* feat: show ' shell running' chip in TUI footer

Adds a footer_shell_chip function that displays a ' shell running'
status chip in the footer's right cluster whenever a foreground shell
command is active via exec_shell. The chip is always visible regardless
of user-configured status items.

Refs: #2194

* feat: auto-collapse finished sub-agents in sidebar

When a sub-agent completes (status = 'done'), its detail lines
(id, steps, duration, progress) are now hidden in the sidebar agents
panel. Only the summary label line is shown, keeping the sidebar
compact. Running agents still show full detail.

Refs: #2195

* feat: refresh Whale dark palette for better contrast

Improve contrast and layer separation in the Whale dark theme:
- Deepen base background for more depth (10,17,32)
- Lighten panel (22,34,56) for clearer distinction from bg
- Lighten elevated surface (36,52,78) for better elevation
- Lighten selection (48,68,100) for clearer selected state
- Boost text hint (138,150,174) and dim (118,130,156) readability
- Brighter border (52,88,145) for better edge definition
- Update tool surface colors for consistency

Refs: #2197

* fix: preserve model name casing in normalize_model_name_for_provider

When the user enters a model name like 'DeepSeek-V4-Flash', the
normalizer was lowercasing it to 'deepseek-v4-flash' via the
canonical_official_deepseek_model_id function. Now the normalizer
preserves the caller's casing when the input already matches a known
model id case-insensitively. Compact aliases like 'deepseek-v4pro'
are still rewritten to 'deepseek-v4-pro'.

Refs: #2109

* feat(web): install download tile with arch detection, SHA256, China mirrors + companion binary fix (#2213)

* fix(web): download both codewhale and codewhale-tui binaries in install snippets

The SNIPPETS map only fetched one binary per platform, causing the
dispatcher to fail with MISSING_COMPANION_BINARY. Every arch now
downloads both codewhale AND codewhale-tui side-by-side.

- macOS/Linux: added second curl + combined chmod/xattr/mv for tui
- Windows: added second Invoke-WebRequest for codewhale-tui.exe
- VERIFY: PowerShell now hashes both binaries; Unix --ignore-missing
  covers all present binaries in a single sha256sum pass

* feat(web): add install download tile with arch detection, SHA256, and China mirrors (#2192)

* feat(sandbox/linux): process hardening — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2214)

* feat(sandbox/linux): add process hardening module — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2183)

* feat(sandbox/linux): seccomp filter + bwrap passthrough

- seccomp: BPF filter whitelisting safe syscalls, denying ptrace/mount/kexec
  and other dangerous syscalls. Uses raw BPF instructions via libc prctl to
  avoid external dependencies (#2182).
- bwrap: optional bubblewrap passthrough when /usr/bin/bwrap is present
  and [sandbox] prefer_bwrap=true in config. Creates read-only rootfs with
  write access limited to the working directory (#2184).
- landlock detect_denial extended to recognize seccomp SIGSYS/"Bad system
  call" patterns alongside existing Landlock EACCES/EPERM detection.
- SandboxManager gains prefer_bwrap field; set_prefer_bwrap on ShellManager.
- EngineConfig gains prefer_bwrap field, wired through main/ui/runtime_threads.
- Diagnostics now reports bwrap_available and cgroup_version.
- config.example.toml documents the prefer_bwrap key.

Pre-existing clippy fixes picked up in the same build:
- collapsible_if in ui.rs version-check
- cmp_owned in goal.rs test
- consecutive str::replace in normalize_auth_mode

Closes #2182, closes #2184

* docs: add cross-links to issue and PR templates in CONTRIBUTING.md (#2215)

- Link .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md from
  the Reporting Issues section
- Link .github/PULL_REQUEST_TEMPLATE.md from the Pull Request Guidelines
  section

* feat(release): bundle platform archives with install scripts (#2216)

- Add bundle job to release workflow that creates per-platform archives
  (tar.gz for Linux/macOS, .zip for Windows) containing both codewhale
  and codewhale-tui binaries plus install scripts
- Create install.bat (Windows) — copies binaries to %USERPROFILE%\bin
- Create install.sh (Unix) — copies binaries to ~/.local/bin
- Windows gets a portable .zip variant without install script
- Release notes updated to promote archives as primary download method
- Individual binaries retained for npm wrapper and scripting

Closes #2193

* fix(web_search): fall back to DuckDuckGo when Bing returns zero results (#2130)

When the configured search provider is Bing and the query returns zero
results (common for technical/compound queries), fall through to the
DuckDuckGo path instead of reporting empty. A provenance message is
surfaced: "Bing returned no results; used DuckDuckGo fallback".

Also adds Security and Code of Conduct cross-links to CONTRIBUTING.md
per the sub-agent renovation (#2203).

* docs: SANDBOX.md threat model + RFCs for persistence and MCP + SandboxExecutor trait

- docs/SANDBOX.md: complete threat model describing each platform's sandbox
  (Seatbelt, Landlock, seccomp, process hardening, bwrap, Windows v1).
  Covers defense-in-depth layering, config keys, denial detection, limitations.
- docs/rfcs/2189-persistence-sqlite.md: RFC for SQLite migration (drafted by sub-agent)
- docs/rfcs/2190-mcp-modularization.md: RFC for MCP crate split into
  protocol/client/server with OAuth support
- crates/tui/src/sandbox/policy.rs: SandboxExecutor trait definition and
  SafetyLevel→SandboxPolicyBehavior mapping function with tests

Closes #2180, closes #2186, closes #2189, closes #2190

* feat: sandbox parity tests + remove sub-agent 100-turn cap

- Add sandbox parity tests covering platform detection, denial patterns,
  bwrap preference, and policy consistency across modes (#2187)
- Remove arbitrary 100-turn sub-agent cap: DEFAULT_MAX_STEPS changed
  from 100 to u32::MAX. Sub-agents now run until they produce a final
  text response, are cancelled by the parent, or hit a configured
  explicit budget (#2034)

Closes #2187, closes #2034
2026-05-26 09:52:22 -05:00

7.1 KiB

RFC: MCP Modularization

Issue: #2190 Status: Draft Date: 2026-05-26

1. Current state

1.1 codewhale-mcp crate (crates/mcp/)

The current MCP implementation lives in a single crate with two responsibilities:

  • MCP client — connects to MCP servers over stdio, manages protocol handshake, tool discovery, and tool invocation. Used by the TUI to surface MCP tools as mcp_<server>_<tool> entries in the tool registry.
  • MCP stdio server — a minimal MCP server that exposes CodeWhale's own tools over stdio for external MCP clients. Used by the codewhale mcp CLI subcommand.

Both the client and server share protocol types (JSON-RPC messages, tool schemas) but have different lifecycle concerns and different callers.

1.2 Integration points

  • crates/tui/src/mcp.rs — MCP client integration: server lifecycle, tool discovery, tool execution forwarding
  • crates/tui/src/mcp_server.rs — MCP stdio server: exposes TUI tools via stdio MCP protocol
  • docs/MCP.md — user-facing documentation

2. Motivation

2.1 Separation of concerns

The client and server share a crate but have no shared code paths at runtime. They import the same protocol types but serve different roles:

  • The client is outbound — it connects to external servers
  • The server is inbound — it accepts connections from external clients

Mixing them in one crate creates unnecessary coupling: changes to the server API recompile the client, and vice versa.

2.2 OAuth support

The current MCP client has no OAuth support. MCP servers that require OAuth (e.g., GitHub, Google) cannot be used. Adding OAuth to the client requires:

  • Token storage (keychain, env-based, or config-based)
  • OAuth flow (device code, PKCE, or client credentials)
  • Token refresh and expiry handling

These concerns are client-side only and should not affect the server crate.

2.3 Reuse outside the TUI

The MCP client is currently embedded in the TUI binary. If we want to use MCP tools from:

  • The app-server (HTTP/SSE runtime API)
  • The codewhale CLI (non-interactive mode)
  • External consumers (library use)

...the client needs to be a standalone crate with a clean public API.

3. Proposed crate split

crates/mcp/           →  crates/mcp-protocol/   (shared types, no I/O)
                          crates/mcp-client/     (client implementation)
                          crates/mcp-server/     (server implementation)

3.1 codewhale-mcp-protocol

Contents: JSON-RPC message types, tool schema types, protocol constants, handshake types, error types. No I/O, no async runtime dependency.

Dependencies: serde, serde_json, codewhale-protocol (for tool schema)

Public API:

pub mod messages;     // JSON-RPC request/response/notification types
pub mod tools;        // MCP tool schema types
pub mod errors;       // MCP error codes
pub mod version;      // Protocol version constants

3.2 codewhale-mcp-client

Contents: MCP client: stdio transport, process management, handshake, tool discovery, tool invocation, OAuth support.

Dependencies: codewhale-mcp-protocol, tokio, serde_json, tracing, oauth2 (new, for OAuth), keyring (optional, for token storage)

Public API:

pub struct McpClient {
    // Configuration
}

impl McpClient {
    pub async fn connect(config: McpClientConfig) -> Result<Self>;
    pub async fn list_tools(&self) -> Result<Vec<ToolSchema>>;
    pub async fn call_tool(&self, name: &str, args: Value) -> Result<Value>;
    pub async fn disconnect(self);
}

pub struct McpClientConfig {
    pub command: String,           // e.g., "npx", "python"
    pub args: Vec<String>,         // e.g., ["-y", "@modelcontextprotocol/server-github"]
    pub env: HashMap<String, String>,
    pub oauth: Option<OAuthConfig>,
    pub timeout: Duration,
}

pub struct OAuthConfig {
    pub provider: OAuthProvider,
    pub client_id: String,
    pub scopes: Vec<String>,
    pub token_storage: TokenStorage,
}

pub enum OAuthProvider {
    Github,
    Google,
    Custom { auth_url: String, token_url: String },
}

3.3 codewhale-mcp-server

Contents: MCP stdio server: accepts connections, exposes tool list, handles tool calls, manages stdio transport.

Dependencies: codewhale-mcp-protocol, codewhale-tools, tokio, serde_json, tracing

Public API:

pub struct McpServer {
    // Tool registry
}

impl McpServer {
    pub fn new(tools: Vec<Arc<dyn ToolSpec>>) -> Self;
    pub async fn serve_stdio(self) -> Result<()>;
    pub async fn serve_sse(self, addr: SocketAddr) -> Result<()>;
}

4. Migration path

Phase 1: Extract protocol crate (non-breaking)

  1. Move shared types from crates/mcp/src/ to crates/mcp-protocol/src/
  2. Re-export from codewhale-mcp for backward compatibility
  3. Update Cargo.toml in codewhale-mcp to depend on codewhale-mcp-protocol

Phase 2: Split client and server (breaking for direct imports)

  1. Create crates/mcp-client/ with client code
  2. Create crates/mcp-server/ with server code
  3. Update codewhale-tui to depend on codewhale-mcp-client
  4. Update codewhale-cli to depend on codewhale-mcp-server
  5. Deprecate codewhale-mcp crate (re-exports from new crates)

Phase 3: Remove legacy crate

  1. Remove crates/mcp/ after a deprecation cycle (one release)

5. OAuth integration

5.1 Token storage

Tokens should be stored securely. Options (in priority order):

  1. OS keychain via keyring crate (macOS Keychain, Windows Credential Manager, Linux Secret Service)
  2. Encrypted file in ~/.codewhale/mcp-credentials/ (fallback)
  3. Environment variable MCP_OAUTH_TOKEN_<PROVIDER>

5.2 OAuth flows

Initial implementation supports:

  • Device Code Flow (GitHub) — user opens a URL, enters a code
  • Client Credentials — for service-to-service MCP servers

Future (deferred):

  • PKCE — for user-facing OAuth with redirect
  • Token refresh — automatic refresh with refresh_token

5.3 Configuration

# ~/.codewhale/config.toml
[mcp.servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]

[mcp.servers.github.oauth]
provider = "github"
client_id = "your-client-id"
scopes = ["repo", "read:org"]

6. Risks and unknowns

Risk Mitigation
Crate proliferation 3 small crates vs 1 medium crate; each has a clear purpose
Breaking internal imports Phase 2 carries codewhale-mcp deprecation shim for one release
OAuth token security OS keychain preferred; encrypted fallback with file permissions
Testing complexity Each crate has its own test suite; integration tests remain in crates/tui/tests/
Dependency bloat oauth2 and keyring are optional features; consumers opt in

7. Out of scope (future RFCs)

  • MCP over HTTP/SSE transport (currently stdio only)
  • MCP server discovery (currently explicit config)
  • MCP tool result streaming (currently request-response)
  • MCP server-side tool approval flows
  • crates/mcp/src/ — current implementation
  • crates/tui/src/mcp.rs — TUI MCP integration
  • crates/tui/src/mcp_server.rs — MCP stdio server
  • docs/MCP.md — user-facing documentation
  • Issue #2190 — this RFC