返回 CodeWhale
2190-mcp-modularization.md
根目录 / docs / rfcs / 2190-mcp-modularization.md
1 # RFC: MCP Modularization
2
3 **Issue:** #2190
4 **Status:** Draft
5 **Date:** 2026-05-26
6
7 ## 1. Current state
8
9 ### 1.1 `codewhale-mcp` crate (`crates/mcp/`)
10
11 The current MCP implementation lives in a single crate with two responsibilities:
12
13 - **MCP client** — connects to MCP servers over stdio, manages protocol handshake,
14 tool discovery, and tool invocation. Used by the TUI to surface MCP tools as
15 `mcp_<server>_<tool>` entries in the tool registry.
16 - **MCP stdio server** — a minimal MCP server that exposes CodeWhale's own tools
17 over stdio for external MCP clients. Used by the `codewhale mcp` CLI subcommand.
18
19 Both the client and server share protocol types (JSON-RPC messages, tool schemas)
20 but have different lifecycle concerns and different callers.
21
22 ### 1.2 Integration points
23
24 - `crates/tui/src/mcp.rs` — MCP client integration: server lifecycle, tool
25 discovery, tool execution forwarding
26 - `crates/tui/src/mcp_server.rs` — MCP stdio server: exposes TUI tools via
27 stdio MCP protocol
28 - `docs/MCP.md` — user-facing documentation
29
30 ## 2. Motivation
31
32 ### 2.1 Separation of concerns
33
34 The client and server share a crate but have no shared code paths at runtime.
35 They import the same protocol types but serve different roles:
36 - The client is **outbound** — it connects to external servers
37 - The server is **inbound** — it accepts connections from external clients
38
39 Mixing them in one crate creates unnecessary coupling: changes to the server
40 API recompile the client, and vice versa.
41
42 ### 2.2 OAuth support
43
44 The current MCP client has no OAuth support. MCP servers that require OAuth
45 (e.g., GitHub, Google) cannot be used. Adding OAuth to the client requires:
46 - Token storage (keychain, env-based, or config-based)
47 - OAuth flow (device code, PKCE, or client credentials)
48 - Token refresh and expiry handling
49
50 These concerns are client-side only and should not affect the server crate.
51
52 ### 2.3 Reuse outside the TUI
53
54 The MCP client is currently embedded in the TUI binary. If we want to use
55 MCP tools from:
56 - The `app-server` (HTTP/SSE runtime API)
57 - The `codewhale` CLI (non-interactive mode)
58 - External consumers (library use)
59
60 ...the client needs to be a standalone crate with a clean public API.
61
62 ## 3. Proposed crate split
63
64 ```
65 crates/mcp/ → crates/mcp-protocol/ (shared types, no I/O)
66 crates/mcp-client/ (client implementation)
67 crates/mcp-server/ (server implementation)
68 ```
69
70 ### 3.1 `codewhale-mcp-protocol`
71
72 **Contents:** JSON-RPC message types, tool schema types, protocol constants,
73 handshake types, error types. No I/O, no async runtime dependency.
74
75 **Dependencies:** `serde`, `serde_json`, `codewhale-protocol` (for tool schema)
76
77 **Public API:**
78 ```rust
79 pub mod messages; // JSON-RPC request/response/notification types
80 pub mod tools; // MCP tool schema types
81 pub mod errors; // MCP error codes
82 pub mod version; // Protocol version constants
83 ```
84
85 ### 3.2 `codewhale-mcp-client`
86
87 **Contents:** MCP client: stdio transport, process management, handshake,
88 tool discovery, tool invocation, OAuth support.
89
90 **Dependencies:** `codewhale-mcp-protocol`, `tokio`, `serde_json`, `tracing`,
91 `oauth2` (new, for OAuth), `keyring` (optional, for token storage)
92
93 **Public API:**
94 ```rust
95 pub struct McpClient {
96 // Configuration
97 }
98
99 impl McpClient {
100 pub async fn connect(config: McpClientConfig) -> Result<Self>;
101 pub async fn list_tools(&self) -> Result<Vec<ToolSchema>>;
102 pub async fn call_tool(&self, name: &str, args: Value) -> Result<Value>;
103 pub async fn disconnect(self);
104 }
105
106 pub struct McpClientConfig {
107 pub command: String, // e.g., "npx", "python"
108 pub args: Vec<String>, // e.g., ["-y", "@modelcontextprotocol/server-github"]
109 pub env: HashMap<String, String>,
110 pub oauth: Option<OAuthConfig>,
111 pub timeout: Duration,
112 }
113
114 pub struct OAuthConfig {
115 pub provider: OAuthProvider,
116 pub client_id: String,
117 pub scopes: Vec<String>,
118 pub token_storage: TokenStorage,
119 }
120
121 pub enum OAuthProvider {
122 Github,
123 Google,
124 Custom { auth_url: String, token_url: String },
125 }
126 ```
127
128 ### 3.3 `codewhale-mcp-server`
129
130 **Contents:** MCP stdio server: accepts connections, exposes tool list,
131 handles tool calls, manages stdio transport.
132
133 **Dependencies:** `codewhale-mcp-protocol`, `codewhale-tools`, `tokio`,
134 `serde_json`, `tracing`
135
136 **Public API:**
137 ```rust
138 pub struct McpServer {
139 // Tool registry
140 }
141
142 impl McpServer {
143 pub fn new(tools: Vec<Arc<dyn ToolSpec>>) -> Self;
144 pub async fn serve_stdio(self) -> Result<()>;
145 pub async fn serve_sse(self, addr: SocketAddr) -> Result<()>;
146 }
147 ```
148
149 ## 4. Migration path
150
151 ### Phase 1: Extract protocol crate (non-breaking)
152
153 1. Move shared types from `crates/mcp/src/` to `crates/mcp-protocol/src/`
154 2. Re-export from `codewhale-mcp` for backward compatibility
155 3. Update `Cargo.toml` in `codewhale-mcp` to depend on `codewhale-mcp-protocol`
156
157 ### Phase 2: Split client and server (breaking for direct imports)
158
159 1. Create `crates/mcp-client/` with client code
160 2. Create `crates/mcp-server/` with server code
161 3. Update `codewhale-tui` to depend on `codewhale-mcp-client`
162 4. Update `codewhale-cli` to depend on `codewhale-mcp-server`
163 5. Deprecate `codewhale-mcp` crate (re-exports from new crates)
164
165 ### Phase 3: Remove legacy crate
166
167 1. Remove `crates/mcp/` after a deprecation cycle (one release)
168
169 ## 5. OAuth integration
170
171 ### 5.1 Token storage
172
173 Tokens should be stored securely. Options (in priority order):
174 1. OS keychain via `keyring` crate (macOS Keychain, Windows Credential Manager,
175 Linux Secret Service)
176 2. Encrypted file in `~/.codewhale/mcp-credentials/` (fallback)
177 3. Environment variable `MCP_OAUTH_TOKEN_<PROVIDER>`
178
179 ### 5.2 OAuth flows
180
181 Initial implementation supports:
182 - **Device Code Flow** (GitHub) — user opens a URL, enters a code
183 - **Client Credentials** — for service-to-service MCP servers
184
185 Future (deferred):
186 - **PKCE** — for user-facing OAuth with redirect
187 - **Token refresh** — automatic refresh with refresh_token
188
189 ### 5.3 Configuration
190
191 ```toml
192 # ~/.codewhale/config.toml
193 [mcp.servers.github]
194 command = "npx"
195 args = ["-y", "@modelcontextprotocol/server-github"]
196
197 [mcp.servers.github.oauth]
198 provider = "github"
199 client_id = "your-client-id"
200 scopes = ["repo", "read:org"]
201 ```
202
203 ## 6. Risks and unknowns
204
205 | Risk | Mitigation |
206 |---|---|
207 | Crate proliferation | 3 small crates vs 1 medium crate; each has a clear purpose |
208 | Breaking internal imports | Phase 2 carries `codewhale-mcp` deprecation shim for one release |
209 | OAuth token security | OS keychain preferred; encrypted fallback with file permissions |
210 | Testing complexity | Each crate has its own test suite; integration tests remain in `crates/tui/tests/` |
211 | Dependency bloat | `oauth2` and `keyring` are optional features; consumers opt in |
212
213 ## 7. Out of scope (future RFCs)
214
215 - MCP over HTTP/SSE transport (currently stdio only)
216 - MCP server discovery (currently explicit config)
217 - MCP tool result streaming (currently request-response)
218 - MCP server-side tool approval flows
219
220 ## Related
221
222 - `crates/mcp/src/` — current implementation
223 - `crates/tui/src/mcp.rs` — TUI MCP integration
224 - `crates/tui/src/mcp_server.rs` — MCP stdio server
225 - `docs/MCP.md` — user-facing documentation
226 - Issue #2190 — this RFC
227
227 lines MARKDOWN