Skip to content

Commit 65b7049

Browse files
committed
feat: enhance application structure and server management
- Integrated GlobalDialog and ConsentDialog into App component for improved user interaction. - Updated Sidebar navigation to redirect users to the manage page instead of the dashboard. - Removed LocalTable component to streamline the manage page and improve performance. - Adjusted client configurations to mark "Cherry Studio" and "mcphub" as free clients. - Refactored SettingsPage to utilize new SettingsSectionContent and SettingsSidebar components for better organization. - Added isActived property to server configurations for enhanced server state management.
1 parent 6eca4b3 commit 65b7049

30 files changed

+1031
-454
lines changed

src-tauri/src/client.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ impl ClientConfig {
9494
PathBuf::from(&home).join("mcp.json")
9595
} else {
9696
let given_path = PathBuf::from(path_str);
97-
if given_path.is_file() || given_path.extension().map_or(false, |ext| ext == "json") {
97+
if given_path.is_file()
98+
|| given_path.extension().map_or(false, |ext| ext == "json")
99+
{
98100
given_path
99101
} else {
100102
given_path.join("mcp.json")

src-tauri/src/json_manager/file_io.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ pub async fn read_json_file(path: &Path) -> Result<Value, String> {
1010
let content_result = fs::read_to_string(&path_buf).await;
1111

1212
match content_result {
13-
Ok(content) => {
14-
task::spawn_blocking(move || {
15-
serde_json::from_str(&content).map_err(|e| format!("Failed to parse JSON: {}", e))
16-
})
17-
.await
18-
.map_err(|e| format!("Failed to run blocking task for JSON parsing: {}", e))?
19-
},
13+
Ok(content) => task::spawn_blocking(move || {
14+
serde_json::from_str(&content).map_err(|e| format!("Failed to parse JSON: {}", e))
15+
})
16+
.await
17+
.map_err(|e| format!("Failed to run blocking task for JSON parsing: {}", e))?,
2018
Err(e) => {
2119
if e.kind() == ErrorKind::NotFound {
2220
Ok(json!({})) // Return empty JSON for Not Found
@@ -35,20 +33,24 @@ pub async fn write_json_file(path: &Path, content: &Value) -> Result<(), String>
3533
// Ensure directory exists
3634
if let Some(parent) = path_buf.parent() {
3735
if !parent.exists() {
38-
fs::create_dir_all(parent).await.map_err(|e| {
39-
format!("Failed to create directory: {}", e)
40-
})?;
36+
fs::create_dir_all(parent)
37+
.await
38+
.map_err(|e| format!("Failed to create directory: {}", e))?;
4139
}
4240
}
4341

4442
// Serialize JSON in a blocking task
4543
let json_string_result = task::spawn_blocking(move || {
4644
serde_json::to_string_pretty(&content_cloned)
4745
.map_err(|e| format!("Failed to serialize JSON: {}", e))
48-
}).await.map_err(|e| format!("Failed to run blocking task for JSON serialization: {}", e))?;
46+
})
47+
.await
48+
.map_err(|e| format!("Failed to run blocking task for JSON serialization: {}", e))?;
4949

5050
let json_string = json_string_result?; // Handle the inner Result from the blocking task
5151

5252
// Write the JSON file asynchronously
53-
fs::write(&path_buf, json_string).await.map_err(|e| format!("Failed to write file: {}", e))
53+
fs::write(&path_buf, json_string)
54+
.await
55+
.map_err(|e| format!("Failed to write file: {}", e))
5456
}

src-tauri/src/json_manager/mod.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ pub mod server_state;
88
pub mod utils;
99

1010
// Re-exports for convenience
11-
pub use file_io::{read_json_file, write_json_file};
12-
pub use server_crud::{add_mcp_server, remove_mcp_server, update_mcp_server, batch_delete_mcp_servers};
13-
pub use server_state::{
14-
disable_mcp_server, enable_mcp_server, update_disabled_mcp_server, list_disabled_servers
15-
};
16-
pub use utils::{normalize_response_key, get_key_by_client, is_per_server_disabled_client};
1711

1812
/// Main JsonManager struct that provides a unified interface for all JSON operations
1913
pub struct JsonManager;
@@ -58,9 +52,13 @@ impl JsonManager {
5852
) -> Result<Value, String> {
5953
server_crud::batch_delete_mcp_servers(path, client, server_names).await
6054
}
61-
55+
6256
// Server state management operations
63-
pub async fn disable_mcp_server(path: &Path, client: &str, name: &str) -> Result<Value, String> {
57+
pub async fn disable_mcp_server(
58+
path: &Path,
59+
client: &str,
60+
name: &str,
61+
) -> Result<Value, String> {
6462
server_state::disable_mcp_server(path, client, name).await
6563
}
6664

@@ -80,4 +78,4 @@ impl JsonManager {
8078
pub async fn list_disabled_servers(path: &Path, client: &str) -> Result<Value, String> {
8179
server_state::list_disabled_servers(path, client).await
8280
}
83-
}
81+
}

src-tauri/src/json_manager/server_crud.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,26 @@ pub async fn update_mcp_server(
6565
}
6666

6767
// Check if server exists in active servers
68-
if json.as_object().unwrap().contains_key(key)
69-
&& json[key].is_object()
70-
&& json[key].as_object().unwrap().contains_key(name) {
68+
if json.as_object().unwrap().contains_key(key)
69+
&& json[key].is_object()
70+
&& json[key].as_object().unwrap().contains_key(name)
71+
{
7172
json[key][name] = config;
7273
}
7374
// Check if server exists in disabled servers
7475
else if json.as_object().unwrap().contains_key("__disabled")
7576
&& json["__disabled"].is_object()
76-
&& json["__disabled"].as_object().unwrap().contains_key(name) {
77+
&& json["__disabled"].as_object().unwrap().contains_key(name)
78+
{
7779
json["__disabled"][name] = config;
7880
}
7981
// If server doesn't exist in either section, add to active servers
8082
else {
8183
if !json.as_object().unwrap().contains_key(key) {
8284
json[key] = json!({});
8385
}
84-
json[key][name] = config; }
86+
json[key][name] = config;
87+
}
8588

8689
write_json_file(path, &json).await?;
8790

@@ -96,7 +99,7 @@ pub async fn batch_delete_mcp_servers(
9699
server_names: Vec<String>,
97100
) -> Result<Value, String> {
98101
use super::utils::is_per_server_disabled_client;
99-
102+
100103
let mut json = read_json_file(path).await?;
101104
let key = get_key_by_client(client);
102105

src-tauri/src/json_manager/server_state.rs

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde_json::{json, Value};
22
use std::path::Path;
33

44
use super::file_io::{read_json_file, write_json_file};
5-
use super::utils::{get_key_by_client, is_per_server_disabled_client, normalize_response_key};
5+
use super::utils::{get_key_by_client, is_per_server_disabled_client, normalize_response_key, is_cherrystudio_client};
66

77
/// Update a disabled MCP server configuration
88
pub async fn update_disabled_mcp_server(
@@ -30,6 +30,22 @@ pub async fn update_disabled_mcp_server(
3030
return normalize_response_key(json, client);
3131
}
3232

33+
// cherrystudio: update mcpServers with isActived: false
34+
if is_cherrystudio_client(client) {
35+
if !json.is_object() {
36+
json = json!({});
37+
}
38+
if !json.as_object().unwrap().contains_key(key) {
39+
json[key] = json!({});
40+
}
41+
// Set config and isActived: false
42+
let mut config_with_isactived = config;
43+
config_with_isactived["isActived"] = json!(false);
44+
json[key][name] = config_with_isactived;
45+
write_json_file(path, &json).await?;
46+
return normalize_response_key(json, client);
47+
}
48+
3349
// Default: update __disabled section
3450
if !json.is_object() {
3551
json = json!({});
@@ -71,6 +87,20 @@ pub async fn disable_mcp_server(path: &Path, client: &str, name: &str) -> Result
7187
return normalize_response_key(json, client);
7288
}
7389

90+
// cherrystudio: set isActived: false in mcpServers
91+
if is_cherrystudio_client(client) {
92+
if !json.as_object().unwrap().contains_key(key)
93+
|| !json[key].is_object()
94+
|| !json[key].as_object().unwrap().contains_key(name)
95+
{
96+
return Err(format!("Server '{}' not found in active servers", name));
97+
}
98+
// Set isActived: false
99+
json[key][name]["isActived"] = json!(false);
100+
write_json_file(path, &json).await?;
101+
return normalize_response_key(json, client);
102+
}
103+
74104
// Default: move to __disabled section // Check if server exists in active servers
75105
if !json.as_object().unwrap().contains_key(key)
76106
|| !json[key].is_object()
@@ -125,6 +155,38 @@ pub async fn enable_mcp_server(path: &Path, client: &str, name: &str) -> Result<
125155
return normalize_response_key(json, client);
126156
}
127157

158+
// cherrystudio: set isActived: true in mcpServers, or move from __disabled if not found
159+
if is_cherrystudio_client(client) {
160+
if json.as_object().unwrap().contains_key(key)
161+
&& json[key].is_object()
162+
&& json[key].as_object().unwrap().contains_key(name)
163+
{
164+
// Set isActived: true
165+
json[key][name]["isActived"] = json!(true);
166+
write_json_file(path, &json).await?;
167+
return normalize_response_key(json, client);
168+
}
169+
// If not found in mcpServers, try to move from __disabled
170+
if json.as_object().unwrap().contains_key("__disabled")
171+
&& json["__disabled"].is_object()
172+
&& json["__disabled"].as_object().unwrap().contains_key(name)
173+
{
174+
let mut server_config = json["__disabled"][name].clone();
175+
server_config["isActived"] = json!(true);
176+
json["__disabled"].as_object_mut().unwrap().remove(name);
177+
if json["__disabled"].as_object().unwrap().is_empty() {
178+
json.as_object_mut().unwrap().remove("__disabled");
179+
}
180+
if !json.as_object().unwrap().contains_key(key) {
181+
json[key] = json!({});
182+
}
183+
json[key][name] = server_config;
184+
write_json_file(path, &json).await?;
185+
return normalize_response_key(json, client);
186+
}
187+
return Err(format!("Server '{}' not found in active or disabled servers", name));
188+
}
189+
128190
// Default: move from __disabled section to active
129191
// Check if server exists in disabled section
130192
if !json.as_object().unwrap().contains_key("__disabled")
@@ -177,7 +239,30 @@ pub async fn list_disabled_servers(path: &Path, client: &str) -> Result<Value, S
177239
if json.is_object() && json.as_object().unwrap().contains_key(key) {
178240
if let Some(servers_obj) = json[key].as_object() {
179241
for (name, server) in servers_obj {
180-
if server.get("disabled").and_then(|v| v.as_bool()).unwrap_or(false) {
242+
if server
243+
.get("disabled")
244+
.and_then(|v| v.as_bool())
245+
.unwrap_or(false)
246+
{
247+
disabled.insert(name.clone(), server.clone());
248+
}
249+
}
250+
}
251+
}
252+
return Ok(Value::Object(disabled));
253+
}
254+
255+
// cherrystudio: return all mcpServers with isActived: false
256+
if is_cherrystudio_client(client) {
257+
let mut disabled = serde_json::Map::new();
258+
if json.is_object() && json.as_object().unwrap().contains_key(key) {
259+
if let Some(servers_obj) = json[key].as_object() {
260+
for (name, server) in servers_obj {
261+
if server
262+
.get("isActived")
263+
.and_then(|v| v.as_bool())
264+
== Some(false)
265+
{
181266
disabled.insert(name.clone(), server.clone());
182267
}
183268
}

src-tauri/src/json_manager/utils.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use serde_json::{json, Value};
1+
use serde_json::Value;
22

33
/// Normalize response key to mcpServers for consistent client API
44
pub fn normalize_response_key(mut json: Value, client: &str) -> Result<Value, String> {
@@ -32,3 +32,8 @@ pub fn get_key_by_client(client: &str) -> &str {
3232
pub fn is_per_server_disabled_client(client: &str) -> bool {
3333
matches!(client, "cline" | "roo_code")
3434
}
35+
36+
/// Returns true if the client is cherrystudio (uses per-server 'isActived' key)
37+
pub fn is_cherrystudio_client(client: &str) -> bool {
38+
client == "cherrystudio"
39+
}

src-tauri/src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ mod git;
1717
mod installer;
1818
mod json_manager;
1919
mod mcp_commands;
20+
mod mcp_crud;
21+
mod mcp_sync;
2022

2123
#[derive(Debug, Serialize, Deserialize)]
2224
struct Config {
@@ -82,15 +84,15 @@ pub fn run() {
8284
cmd::write_json_file,
8385
cmd::get_app_path,
8486
cmd::check_mcplinker_config_exists,
85-
mcp_commands::add_mcp_server,
86-
mcp_commands::remove_mcp_server,
87-
mcp_commands::update_mcp_server,
87+
mcp_crud::add_mcp_server,
88+
mcp_crud::remove_mcp_server,
89+
mcp_crud::update_mcp_server,
90+
mcp_crud::batch_delete_mcp_servers,
8891
mcp_commands::disable_mcp_server,
8992
mcp_commands::enable_mcp_server,
9093
mcp_commands::list_disabled_servers,
91-
mcp_commands::sync_mcp_config,
92-
mcp_commands::batch_delete_mcp_servers,
9394
mcp_commands::update_disabled_mcp_server,
95+
mcp_sync::sync_mcp_config,
9496
installer::check_command_exists,
9597
installer::install_command,
9698
get_path_env,

0 commit comments

Comments
 (0)