|
| 1 | +//! Tauri commands for the per-Space built-in server config. |
| 2 | +//! |
| 3 | +//! Built-in servers (today: "Tool Optimization", the `mcpmux_*` tools) and |
| 4 | +//! their individual tools are enabled/disabled **per Space**. The descriptors |
| 5 | +//! (ids, names, tool sets) come from `mcpmux_core::builtin_servers()`; the |
| 6 | +//! per-Space enable state comes from `SpaceBuiltinConfigRepository`. Toggling |
| 7 | +//! emits `BuiltinServerConfigChanged` so the gateway re-pushes |
| 8 | +//! `tools/list_changed` to that Space's connected clients. |
| 9 | +
|
| 10 | +use std::sync::Arc; |
| 11 | + |
| 12 | +use mcpmux_core::DomainEvent; |
| 13 | +use serde::Serialize; |
| 14 | +use tauri::State; |
| 15 | +use tokio::sync::RwLock; |
| 16 | +use uuid::Uuid; |
| 17 | + |
| 18 | +use super::gateway::GatewayAppState; |
| 19 | +use crate::state::AppState; |
| 20 | + |
| 21 | +/// One tool of a built-in server, with its per-Space enabled state. |
| 22 | +#[derive(Debug, Clone, Serialize)] |
| 23 | +pub struct BuiltinToolDto { |
| 24 | + pub name: String, |
| 25 | + pub description: String, |
| 26 | + /// Mutating tool — gated behind a native approval dialog at call time. |
| 27 | + pub write: bool, |
| 28 | + pub enabled: bool, |
| 29 | +} |
| 30 | + |
| 31 | +/// A built-in server as configured for a specific Space. |
| 32 | +#[derive(Debug, Clone, Serialize)] |
| 33 | +pub struct BuiltinServerDto { |
| 34 | + pub id: String, |
| 35 | + pub name: String, |
| 36 | + pub description: String, |
| 37 | + /// Whether this built-in server is enabled for the Space. |
| 38 | + pub enabled: bool, |
| 39 | + pub tools: Vec<BuiltinToolDto>, |
| 40 | +} |
| 41 | + |
| 42 | +/// Publish `BuiltinServerConfigChanged` so MCPNotifier re-pushes |
| 43 | +/// `tools/list_changed` to the Space's peers. Best-effort: gateway not running |
| 44 | +/// (no subscribers) is a normal startup condition and must not fail the toggle. |
| 45 | +async fn emit_builtin_changed(gateway_state: &Arc<RwLock<GatewayAppState>>, space_id: Uuid) { |
| 46 | + let gw_state = gateway_state.read().await; |
| 47 | + if let Some(ref gw) = gw_state.gateway_state { |
| 48 | + gw.read() |
| 49 | + .await |
| 50 | + .emit_domain_event(DomainEvent::BuiltinServerConfigChanged { space_id }); |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +/// List every built-in server with its per-Space enable state and per-tool |
| 55 | +/// toggles. Combines the static descriptors with the Space's stored overrides |
| 56 | +/// (absence of an override = the descriptor default / tool-on). |
| 57 | +#[tauri::command] |
| 58 | +pub async fn list_builtin_servers( |
| 59 | + space_id: String, |
| 60 | + state: State<'_, AppState>, |
| 61 | +) -> Result<Vec<BuiltinServerDto>, String> { |
| 62 | + let repo = &state.space_builtin_config_repository; |
| 63 | + let mut out = Vec::new(); |
| 64 | + for d in mcpmux_core::builtin_servers() { |
| 65 | + let enabled = repo |
| 66 | + .server_enabled_override(&space_id, d.id) |
| 67 | + .await |
| 68 | + .map_err(|e| e.to_string())? |
| 69 | + .unwrap_or(d.default_enabled); |
| 70 | + let disabled = repo |
| 71 | + .disabled_tools(&space_id, d.id) |
| 72 | + .await |
| 73 | + .map_err(|e| e.to_string())?; |
| 74 | + let tools = d |
| 75 | + .tools |
| 76 | + .iter() |
| 77 | + .map(|t| BuiltinToolDto { |
| 78 | + name: t.name.to_string(), |
| 79 | + description: t.description.to_string(), |
| 80 | + write: t.write, |
| 81 | + enabled: !disabled.iter().any(|n| n == t.name), |
| 82 | + }) |
| 83 | + .collect(); |
| 84 | + out.push(BuiltinServerDto { |
| 85 | + id: d.id.to_string(), |
| 86 | + name: d.name.to_string(), |
| 87 | + description: d.description.to_string(), |
| 88 | + enabled, |
| 89 | + tools, |
| 90 | + }); |
| 91 | + } |
| 92 | + Ok(out) |
| 93 | +} |
| 94 | + |
| 95 | +/// Enable/disable a built-in server for a Space. |
| 96 | +#[tauri::command] |
| 97 | +pub async fn set_builtin_server_enabled( |
| 98 | + space_id: String, |
| 99 | + server_id: String, |
| 100 | + enabled: bool, |
| 101 | + state: State<'_, AppState>, |
| 102 | + gateway_state: State<'_, Arc<RwLock<GatewayAppState>>>, |
| 103 | +) -> Result<(), String> { |
| 104 | + let sid = Uuid::parse_str(&space_id).map_err(|e| format!("bad space_id: {e}"))?; |
| 105 | + state |
| 106 | + .space_builtin_config_repository |
| 107 | + .set_server_enabled(&space_id, &server_id, enabled) |
| 108 | + .await |
| 109 | + .map_err(|e| e.to_string())?; |
| 110 | + emit_builtin_changed(gateway_state.inner(), sid).await; |
| 111 | + Ok(()) |
| 112 | +} |
| 113 | + |
| 114 | +/// Enable/disable a single tool of a built-in server for a Space. |
| 115 | +#[tauri::command] |
| 116 | +pub async fn set_builtin_tool_enabled( |
| 117 | + space_id: String, |
| 118 | + server_id: String, |
| 119 | + tool_name: String, |
| 120 | + enabled: bool, |
| 121 | + state: State<'_, AppState>, |
| 122 | + gateway_state: State<'_, Arc<RwLock<GatewayAppState>>>, |
| 123 | +) -> Result<(), String> { |
| 124 | + let sid = Uuid::parse_str(&space_id).map_err(|e| format!("bad space_id: {e}"))?; |
| 125 | + state |
| 126 | + .space_builtin_config_repository |
| 127 | + .set_tool_enabled(&space_id, &server_id, &tool_name, enabled) |
| 128 | + .await |
| 129 | + .map_err(|e| e.to_string())?; |
| 130 | + emit_builtin_changed(gateway_state.inner(), sid).await; |
| 131 | + Ok(()) |
| 132 | +} |
0 commit comments