Skip to content

Commit e1c0168

Browse files
committed
feat: add new client options and enhance JSON handling for vscode and mcphub
- Added new client options: Cline, Vscode, and mcphub.nvim to the client selector. - Updated EnvEditor to streamline environment variable handling. - Modified needspathClient to include Vscode. - Introduced hasViewed property in ServerType for tracking user interactions. - Enhanced client configuration paths for Cline and Vscode in the Rust backend. - Improved JSON management functions to normalize response keys for client consistency. docs: Add more client to readme
1 parent 2c4f31e commit e1c0168

File tree

13 files changed

+100
-46
lines changed

13 files changed

+100
-46
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
[![GitHub last commit](https://img.shields.io/github/last-commit/milisp/mcp-linker)](https://github.com/milisp/mcp-linker/commits)
2626
![build](https://github.com/milisp/mcp-linker/actions/workflows/tauri-ci-win.yml/badge.svg)
2727

28-
> Add MCP servers to Claude Desktop, Cursor, and Windsurf in two clicks. Cross-platform. Tauri GUI. Server management included.
28+
> Add MCP servers to Claude Desktop, Cursor, Windsurf, VS Code, Cline, MCPHub.nvim, and more — in two clicks. Cross-platform. Tauri GUI. Server management included.
2929
3030
---
3131

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"author": "milisp",
99
"license": "MIT",
1010
"private": true,
11-
"version": "1.2.2",
11+
"version": "1.2.3",
1212
"type": "module",
1313
"scripts": {
1414
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mcp-linker"
3-
version = "1.2.0"
3+
version = "1.2.3"
44
description = "Easily connect and manage MCP servers for Claude and other clients"
55
authors = ["milisp"]
66
edition = "2021"

src-tauri/src/client.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,31 @@ impl ClientConfig {
2727
PathBuf::from("")
2828
}
2929
}
30+
("cline", _) => {
31+
#[cfg(target_os = "macos")]
32+
{
33+
PathBuf::from(home)
34+
.join("Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json")
35+
}
36+
#[cfg(not(any(target_os = "macos")))]
37+
{
38+
PathBuf::from("")
39+
}
40+
}
41+
("vscode", Some(base_path)) => {
42+
// For Cursor, we use the provided path + .cursor/mcp.json
43+
PathBuf::from(base_path).join(".vscode/mcp.json")
44+
}
3045
("cursor", Some(base_path)) => {
3146
// For Cursor, we use the provided path + .cursor/mcp.json
3247
PathBuf::from(base_path).join(".cursor/mcp.json")
3348
}
3449
("cursor", None) => {
3550
PathBuf::from(home).join(".cursor/mcp.json") // 修复:添加默认路径
3651
}
52+
("mcphub", None) => {
53+
PathBuf::from(home).join(".config/mcphub/servers.json")
54+
}
3755
("windsurf", _) => {
3856
PathBuf::from(home)
3957
.join(".codeium/windsurf/mcp_config.json")

src-tauri/src/cmd.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@ pub async fn read_json_file(client_name: String, path: Option<String>) -> Result
1111
return Err(format!("File not found: {}", file_path.display()));
1212
}
1313

14-
JsonManager::read_json_file(file_path)
14+
let json = JsonManager::read_json_file(file_path)?;
15+
// Normalize the response for client consistency
16+
if client_name == "vscode" && json.is_object() {
17+
let mut json_clone = json.clone();
18+
if json_clone.as_object().unwrap().contains_key("servers") &&
19+
!json_clone.as_object().unwrap().contains_key("mcpServers") {
20+
json_clone["mcpServers"] = json_clone["servers"].clone();
21+
}
22+
Ok(json_clone)
23+
} else {
24+
Ok(json)
25+
}
1526
}
1627

1728
#[tauri::command]
@@ -44,7 +55,7 @@ pub async fn add_mcp_server(
4455
let app_config = ClientConfig::new(&client_name, path.as_deref());
4556
let file_path = app_config.get_path();
4657

47-
JsonManager::add_mcp_server(file_path, &server_name, server_config)
58+
JsonManager::add_mcp_server(file_path, &client_name, &server_name, server_config)
4859
}
4960

5061
#[tauri::command]
@@ -56,7 +67,7 @@ pub async fn remove_mcp_server(
5667
let app_config = ClientConfig::new(&client_name, path.as_deref());
5768
let file_path = app_config.get_path();
5869

59-
JsonManager::remove_mcp_server(file_path, &server_name)
70+
JsonManager::remove_mcp_server(file_path, &client_name, &server_name)
6071
}
6172

6273
#[tauri::command]
@@ -69,5 +80,5 @@ pub async fn update_mcp_server(
6980
let app_config = ClientConfig::new(&client_name, path.as_deref());
7081
let file_path = app_config.get_path();
7182

72-
JsonManager::update_mcp_server(file_path, &server_name, server_config)
83+
JsonManager::update_mcp_server(file_path, &client_name, &server_name, server_config)
7384
}

src-tauri/src/json_manager.rs

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ use std::path::Path;
66
pub struct JsonManager;
77

88
impl JsonManager {
9+
// Normalize response key to mcpServers for consistent client API
10+
fn normalize_response_key(mut json: Value, client: &str) -> Result<Value, String> {
11+
if client == "vscode" && json.is_object() {
12+
let servers_key = "servers";
13+
let mcp_servers_key = "mcpServers";
14+
15+
// If json has "servers" key but not "mcpServers"
16+
if json.as_object().unwrap().contains_key(servers_key) &&
17+
!json.as_object().unwrap().contains_key(mcp_servers_key) {
18+
// Clone the content from servers to mcpServers
19+
let servers_value = json[servers_key].clone();
20+
json[mcp_servers_key] = servers_value;
21+
}
22+
}
23+
24+
Ok(json)
25+
}
26+
927
pub fn read_json_file(path: &Path) -> Result<Value, String> {
1028
match fs::read_to_string(path) {
1129
Ok(content) => match serde_json::from_str(&content) {
@@ -45,64 +63,67 @@ impl JsonManager {
4563
}
4664
}
4765

48-
pub fn add_mcp_server(path: &Path, name: &str, config: Value) -> Result<Value, String> {
66+
fn get_key_by_client(client: &str) -> &str {
67+
if client == "vscode" {
68+
"servers"
69+
} else {
70+
"mcpServers"
71+
}
72+
}
73+
74+
pub fn add_mcp_server(path: &Path, client: &str, name: &str, config: Value) -> Result<Value, String> {
4975
let mut json = Self::read_json_file(path)?;
76+
let key = Self::get_key_by_client(client);
5077

51-
// Ensure mcpServers exists
5278
if !json.is_object() {
5379
json = json!({});
5480
}
5581

56-
if !json.as_object().unwrap().contains_key("mcpServers") {
57-
json["mcpServers"] = json!({});
82+
if !json.as_object().unwrap().contains_key(key) {
83+
json[key] = json!({});
5884
}
5985

60-
// Add new server
61-
json["mcpServers"][name] = config;
86+
json[key][name] = config;
6287

63-
// Write back to file
6488
Self::write_json_file(path, &json)?;
65-
66-
Ok(json)
89+
90+
// Normalize response key to mcpServers for client
91+
Self::normalize_response_key(json, client)
6792
}
6893

69-
pub fn remove_mcp_server(path: &Path, name: &str) -> Result<Value, String> {
94+
pub fn remove_mcp_server(path: &Path, client: &str, name: &str) -> Result<Value, String> {
7095
let mut json = Self::read_json_file(path)?;
96+
let key = Self::get_key_by_client(client);
7197

72-
// Check if mcpServers exists
73-
if json.is_object() && json.as_object().unwrap().contains_key("mcpServers") {
74-
if json["mcpServers"].is_object()
75-
&& json["mcpServers"].as_object().unwrap().contains_key(name)
76-
{
77-
json["mcpServers"].as_object_mut().unwrap().remove(name);
98+
if json.is_object() && json.as_object().unwrap().contains_key(key) {
99+
if json[key].is_object() && json[key].as_object().unwrap().contains_key(name) {
100+
json[key].as_object_mut().unwrap().remove(name);
78101
}
79102
}
80103

81-
// Write back to file
82104
Self::write_json_file(path, &json)?;
83-
84-
Ok(json)
105+
106+
// Normalize response key to mcpServers for client
107+
Self::normalize_response_key(json, client)
85108
}
86109

87-
pub fn update_mcp_server(path: &Path, name: &str, config: Value) -> Result<Value, String> {
110+
pub fn update_mcp_server(path: &Path, client: &str, name: &str, config: Value) -> Result<Value, String> {
88111
let mut json = Self::read_json_file(path)?;
112+
let key = Self::get_key_by_client(client);
89113

90-
// Check if mcpServers exists
91-
if json.is_object() && json.as_object().unwrap().contains_key("mcpServers") {
92-
if json["mcpServers"].is_object()
93-
&& json["mcpServers"].as_object().unwrap().contains_key(name)
94-
{
95-
json["mcpServers"][name] = config;
114+
if json.is_object() && json.as_object().unwrap().contains_key(key) {
115+
if json[key].is_object() && json[key].as_object().unwrap().contains_key(name) {
116+
json[key][name] = config;
96117
} else {
97-
return Self::add_mcp_server(path, name, config);
118+
return Self::add_mcp_server(path, client, name, config);
98119
}
99120
} else {
100-
return Self::add_mcp_server(path, name, config);
121+
return Self::add_mcp_server(path, client, name, config);
101122
}
102123

103-
// Write back to file
104124
Self::write_json_file(path, &json)?;
105-
106-
Ok(json)
125+
126+
// Normalize response key to mcpServers for client
127+
Self::normalize_response_key(json, client)
107128
}
108129
}

src-tauri/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use std::env;
1111

1212
#[derive(Debug, Serialize, Deserialize)]
1313
struct Config {
14-
#[serde(rename = "mcpServers")]
15-
mcp_servers: Value, // 允许动态 JSON 结构
14+
#[serde(rename = "mcpServers", alias = "servers")]
15+
mcp_servers: Value,
1616
}
1717

1818
mod client;

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/v2/config.json",
33
"productName": "MCPLinker",
4-
"version": "1.2.2",
4+
"version": "1.2.3",
55
"identifier": "com.milisp.mcplinker",
66
"build": {
77
"beforeDevCommand": "bun run dev",

src/components/client-selector.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ const clientOptions: ClientOption[] = [
1515
{ id: "claude", name: "Claude" },
1616
{ id: "cursor", name: "Cursor" },
1717
{ id: "windsurf", name: "Windsurf" },
18+
{ id: "cline", name: "Cline" },
19+
{ id: "vscode", name: "Vscode" },
20+
{ id: "mcphub", name: "mcphub.nvim" },
1821
{ id: "custom", name: "Custom" },
1922
];
2023

0 commit comments

Comments
 (0)