From 8c710fc7bffeb9af3d4e053f54f5c69fac15a5f0 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Tue, 10 Mar 2026 09:51:47 -0700 Subject: [PATCH] Auto-pull MCP Docker images and add mode hints to MCP UI - Automatically pull missing Docker images for MCP servers before starting containers, with progress streamed to the container progress modal - Add contextual mode descriptions to MCP server cards explaining where commands run (project container vs separate MCP container) - Clarify that HTTP+Docker URLs are auto-generated using the container hostname on the project network, not localhost Co-Authored-By: Claude Opus 4.6 --- .../src/commands/project_commands.rs | 22 +++++++++++++++++++ app/src/components/mcp/McpServerCard.tsx | 12 ++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/src-tauri/src/commands/project_commands.rs b/app/src-tauri/src/commands/project_commands.rs index 9b1c67f..326fd34 100644 --- a/app/src-tauri/src/commands/project_commands.rs +++ b/app/src-tauri/src/commands/project_commands.rs @@ -202,6 +202,28 @@ pub async fn start_project_container( // Set up Docker network and MCP containers if needed let network_name = if !docker_mcp.is_empty() { + // Pull any missing MCP Docker images before starting containers + for server in &docker_mcp { + if let Some(ref image) = server.docker_image { + if !docker::image_exists(image).await.unwrap_or(false) { + emit_progress( + &app_handle, + &project_id, + &format!("Pulling MCP image for '{}'...", server.name), + ); + let image_clone = image.clone(); + let app_clone = app_handle.clone(); + let pid_clone = project_id.clone(); + let sname = server.name.clone(); + docker::pull_image(&image_clone, move |msg| { + emit_progress(&app_clone, &pid_clone, &format!("[{}] {}", sname, msg)); + }).await.map_err(|e| { + format!("Failed to pull MCP image '{}' for '{}': {}", image, server.name, e) + })?; + } + } + } + emit_progress(&app_handle, &project_id, "Setting up MCP network..."); let net = docker::ensure_project_network(&project.id).await?; emit_progress(&app_handle, &project_id, "Starting MCP containers..."); diff --git a/app/src/components/mcp/McpServerCard.tsx b/app/src/components/mcp/McpServerCard.tsx index f886d2b..833bcd2 100644 --- a/app/src/components/mcp/McpServerCard.tsx +++ b/app/src/components/mcp/McpServerCard.tsx @@ -147,7 +147,7 @@ export default function McpServerCard({ server, onUpdate, onRemove }: Props) { className={inputCls} />

- Set a Docker image to run this MCP server as a container. Leave empty for manual mode. + Set a Docker image to run this MCP server in its own container. Leave empty to run commands inside the project container. Images are pulled automatically if not present.

@@ -171,6 +171,14 @@ export default function McpServerCard({ server, onUpdate, onRemove }: Props) { + {/* Mode description */} +

+ {transportType === "stdio" && isDocker && "Runs via docker exec in a separate MCP container."} + {transportType === "stdio" && !isDocker && "Runs inside the project container (e.g. npx commands)."} + {transportType === "http" && isDocker && "Runs in a separate container, reached by hostname on the project network."} + {transportType === "http" && !isDocker && "Connects to an MCP server at the URL you specify."} +

+ {/* Container Port (HTTP+Docker only) */} {transportType === "http" && isDocker && (
@@ -183,7 +191,7 @@ export default function McpServerCard({ server, onUpdate, onRemove }: Props) { className={inputCls} />

- Port inside the MCP container (default: 3000) + Port the MCP server listens on inside its container. The URL is auto-generated as http://<container>:<port>/mcp on the project network.

)}