mirror of
https://github.com/spartanz51/tutabridge.git
synced 2026-06-24 10:54:32 +02:00
Read-only MCP server (HTTP, GUI-controlled)
Expose the mailbox to an LLM client (Claude Desktop / Code) over an in-process MCP server, so the bridge itself hosts it and the GUI controls it live. Strictly read-only: there is no tool that sends, moves, deletes or mutates mail — by design and asserted in tests. Transport: Streamable HTTP (MCP 2025-06-18) on a single POST /mcp endpoint bound to 127.0.0.1, answering each JSON-RPC request with application/json (no SSE — the server never pushes). Auth is a bearer token (the bridge password); the Origin header is validated to block DNS-rebinding. Permission tiers (config.McpPermission, default Disabled = server off): - Metadata — folders, metadata search (subject/sender/date), headers only. - Full — the above plus full-text body search and message body text. Tools: list_folders, search_messages, list_unread, get_message. Search combines subject/sender always and the encrypted FTS body index under Full; get_message returns headers always and body only under Full. Wiring: spawned in-process by both the CLI (main.rs) and the GUI bridge task (bridge.rs); a Disabled tier makes serve() a no-op, and it is kept out of the select! so it never triggers teardown. GUI gains an MCP section (tier selector, port, full-read warning, "copy client config" button) and a get_mcp_client_config command that emits the ready-to-paste client snippet. Validated live on a ~19k-message mailbox: initialize / tools/list / tools/call all conform; 401 without the bearer token, 403 on a foreign Origin, 202 on notifications; list_folders, body search and get_message (HTML stripped to text) all return correctly. 240 unit tests.
This commit is contained in:
@@ -90,6 +90,26 @@ pub async fn regenerate_bridge_password() -> Result<String, String> {
|
||||
config::regenerate_bridge_password(&mut cfg).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Build the MCP client config snippet (URL + bearer token) to paste into
|
||||
/// Claude Desktop / Code. Read-only; reflects the saved port + bridge password.
|
||||
#[tauri::command]
|
||||
pub async fn get_mcp_client_config() -> Result<String, String> {
|
||||
let cfg = config::load_config()
|
||||
.map_err(|e| e.to_string())?
|
||||
.unwrap_or_default();
|
||||
let token = cfg.bridge_password.unwrap_or_default();
|
||||
let snippet = serde_json::json!({
|
||||
"mcpServers": {
|
||||
"tutabridge": {
|
||||
"type": "http",
|
||||
"url": format!("http://127.0.0.1:{}/mcp", cfg.mcp_port),
|
||||
"headers": { "Authorization": format!("Bearer {token}") }
|
||||
}
|
||||
}
|
||||
});
|
||||
serde_json::to_string_pretty(&snippet).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Export every mail to `output_dir` as a tree of `.eml` files. Requires the
|
||||
/// bridge to be running (reuses its live session + cache). Streams progress
|
||||
/// via `bridge://backup-progress` events and resolves with the final stats.
|
||||
|
||||
@@ -35,6 +35,7 @@ fn main() {
|
||||
commands::get_bridge_password,
|
||||
commands::regenerate_bridge_password,
|
||||
commands::export_mails,
|
||||
commands::get_mcp_client_config,
|
||||
])
|
||||
.setup(|app| {
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
Reference in New Issue
Block a user