feat: add MCP server support with global library and per-project toggles
All checks were successful
Build App / build-macos (push) Successful in 2m20s
Build App / build-windows (push) Successful in 3m21s
Build App / build-linux (push) Successful in 5m8s
Build Container / build-container (push) Successful in 1m4s
Sync Release to GitHub / sync-release (release) Successful in 2s
All checks were successful
Build App / build-macos (push) Successful in 2m20s
Build App / build-windows (push) Successful in 3m21s
Build App / build-linux (push) Successful in 5m8s
Build Container / build-container (push) Successful in 1m4s
Sync Release to GitHub / sync-release (release) Successful in 2s
Add Model Context Protocol (MCP) server configuration support. Users can define MCP servers globally (new sidebar tab) and enable them per-project. Enabled servers are injected into containers as MCP_SERVERS_JSON env var and merged into ~/.claude.json by the entrypoint. Backend: McpServer model, McpStore (JSON + atomic writes), 4 CRUD commands, container injection with fingerprint-based recreation detection. Frontend: MCP sidebar tab, McpPanel/McpServerCard components, useMcpServers hook, per-project MCP checkboxes in ProjectCard config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
38
app/src-tauri/src/commands/mcp_commands.rs
Normal file
38
app/src-tauri/src/commands/mcp_commands.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use tauri::State;
|
||||
|
||||
use crate::models::McpServer;
|
||||
use crate::AppState;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_mcp_servers(state: State<'_, AppState>) -> Result<Vec<McpServer>, String> {
|
||||
Ok(state.mcp_store.list())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_mcp_server(
|
||||
name: String,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<McpServer, String> {
|
||||
let name = name.trim().to_string();
|
||||
if name.is_empty() {
|
||||
return Err("MCP server name cannot be empty.".to_string());
|
||||
}
|
||||
let server = McpServer::new(name);
|
||||
state.mcp_store.add(server)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn update_mcp_server(
|
||||
server: McpServer,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<McpServer, String> {
|
||||
state.mcp_store.update(server)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn remove_mcp_server(
|
||||
server_id: String,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<(), String> {
|
||||
state.mcp_store.remove(&server_id)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod docker_commands;
|
||||
pub mod mcp_commands;
|
||||
pub mod project_commands;
|
||||
pub mod settings_commands;
|
||||
pub mod terminal_commands;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use tauri::{Emitter, State};
|
||||
|
||||
use crate::docker;
|
||||
use crate::models::{container_config, AuthMode, Project, ProjectPath, ProjectStatus};
|
||||
use crate::models::{container_config, AuthMode, McpServer, Project, ProjectPath, ProjectStatus};
|
||||
use crate::storage::secure;
|
||||
use crate::AppState;
|
||||
|
||||
@@ -142,6 +142,12 @@ pub async fn start_project_container(
|
||||
let settings = state.settings_store.get();
|
||||
let image_name = container_config::resolve_image_name(&settings.image_source, &settings.custom_image_name);
|
||||
|
||||
// Resolve enabled MCP servers for this project
|
||||
let all_mcp_servers = state.mcp_store.list();
|
||||
let enabled_mcp: Vec<McpServer> = project.enabled_mcp_servers.iter()
|
||||
.filter_map(|id| all_mcp_servers.iter().find(|s| &s.id == id).cloned())
|
||||
.collect();
|
||||
|
||||
// Validate auth mode requirements
|
||||
if project.auth_mode == AuthMode::Bedrock {
|
||||
let bedrock = project.bedrock_config.as_ref()
|
||||
@@ -180,6 +186,7 @@ pub async fn start_project_container(
|
||||
settings.global_claude_instructions.as_deref(),
|
||||
&settings.global_custom_env_vars,
|
||||
settings.timezone.as_deref(),
|
||||
&enabled_mcp,
|
||||
).await.unwrap_or(false);
|
||||
|
||||
if needs_recreate {
|
||||
@@ -210,6 +217,7 @@ pub async fn start_project_container(
|
||||
settings.global_claude_instructions.as_deref(),
|
||||
&settings.global_custom_env_vars,
|
||||
settings.timezone.as_deref(),
|
||||
&enabled_mcp,
|
||||
).await?;
|
||||
emit_progress(&app_handle, &project_id, "Starting container...");
|
||||
docker::start_container(&new_id).await?;
|
||||
@@ -241,6 +249,7 @@ pub async fn start_project_container(
|
||||
settings.global_claude_instructions.as_deref(),
|
||||
&settings.global_custom_env_vars,
|
||||
settings.timezone.as_deref(),
|
||||
&enabled_mcp,
|
||||
).await?;
|
||||
emit_progress(&app_handle, &project_id, "Starting container...");
|
||||
docker::start_container(&new_id).await?;
|
||||
|
||||
Reference in New Issue
Block a user