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

227 lines
7.1 KiB
Markdown

# 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:**
```rust
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:**
```rust
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:**
```rust
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
```toml
# ~/.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
## Related
- `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