Remove MCP backend, entrypoint injection, and docs; add migration shim
Completes the removal begun in the previous commit. Backend: deletes models/mcp_server.rs, storage/mcp_store.rs and commands/mcp_commands.rs, the McpStore on AppState, the four IPC handlers, Project::enabled_mcp_servers, build_mcp_servers_json(), compute_mcp_fingerprint(), the MCP_SERVERS_JSON env injection, the mcp-fingerprint label, and the whole MCP container lifecycle. create_container() and container_needs_recreation() lose their mcp_servers/network_name parameters. Container: entrypoint.sh no longer merges MCP_SERVERS_JSON into ~/.claude.json. MCP_SERVERS_JSON stays in the reserved env blocklist. Security: the Docker socket is no longer auto-mounted for stdio+Docker MCP servers — it now mounts only when allow_docker_access is set. Migration: old containers were created with network_mode=triple-c-net-<projectId> and refuse to start once that network is gone. docker/network.rs becomes docker/legacy_cleanup.rs with label-driven, best-effort removal of leftover MCP containers and the per-project network, called on both delete and recreate. container_needs_recreation() now forces a rebuild for any container carrying a non-empty triple-c.mcp-fingerprint label or attached to a triple-c-net-* network, moving it onto the default bridge. Both can be dropped a release later. Docs: drops the MCP sections from README/HOW-TO-USE/TECHNICAL and adds a short note pointing at Claude Code's native `claude mcp` / `/mcp` / .mcp.json instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
//! One-release migration shim for the removed built-in MCP feature.
|
||||
//!
|
||||
//! Older releases created a per-project user-defined bridge network
|
||||
//! (`triple-c-net-<projectId>`) plus one container per Docker-backed MCP
|
||||
//! server, and attached the project container to that network. Now that MCP
|
||||
//! support is gone, those leftovers have to be torn down — a container whose
|
||||
//! `NetworkMode` names a network that no longer exists refuses to start, so
|
||||
//! the cleanup is paired with a forced container recreation (see
|
||||
//! `container_needs_recreation`).
|
||||
//!
|
||||
//! Everything here is best-effort: failures are logged and never abort the
|
||||
//! caller, and absent resources are a silent no-op. This module can be deleted
|
||||
//! a release after all users have migrated.
|
||||
|
||||
use bollard::container::{ListContainersOptions, RemoveContainerOptions};
|
||||
use bollard::network::InspectNetworkOptions;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::client::get_docker;
|
||||
|
||||
/// Network name used by the old MCP implementation for a project.
|
||||
fn legacy_network_name(project_id: &str) -> String {
|
||||
format!("triple-c-net-{}", project_id)
|
||||
}
|
||||
|
||||
/// Force-remove every leftover MCP server container.
|
||||
///
|
||||
/// Matched by the `triple-c.mcp-server` label rather than by name, so
|
||||
/// containers survive even if the MCP server definitions they came from are
|
||||
/// already gone from storage. Best-effort: errors are logged and skipped.
|
||||
pub async fn remove_legacy_mcp_containers(project_id: &str) {
|
||||
let docker = match get_docker() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"Skipping legacy MCP container cleanup for project {}: {}",
|
||||
project_id,
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let filters: HashMap<String, Vec<String>> = HashMap::from([(
|
||||
"label".to_string(),
|
||||
vec!["triple-c.mcp-server".to_string()],
|
||||
)]);
|
||||
|
||||
let containers = match docker
|
||||
.list_containers(Some(ListContainersOptions {
|
||||
all: true,
|
||||
filters,
|
||||
..Default::default()
|
||||
}))
|
||||
.await
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to list legacy MCP containers: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for container in containers {
|
||||
let Some(id) = container.id else { continue };
|
||||
match docker
|
||||
.remove_container(
|
||||
&id,
|
||||
Some(RemoveContainerOptions {
|
||||
force: true,
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => log::info!("Removed legacy MCP container {}", id),
|
||||
Err(e) => log::warn!("Failed to remove legacy MCP container {}: {}", id, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the old per-project Docker network, disconnecting any remaining
|
||||
/// members first (a network with attached endpoints cannot be deleted).
|
||||
///
|
||||
/// Silent no-op when the network does not exist. Best-effort: errors are
|
||||
/// logged and never propagated.
|
||||
pub async fn remove_legacy_project_network(project_id: &str) {
|
||||
let docker = match get_docker() {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
log::debug!(
|
||||
"Skipping legacy network cleanup for project {}: {}",
|
||||
project_id,
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let network_name = legacy_network_name(project_id);
|
||||
|
||||
// Inspect to discover connected containers; absence means nothing to do.
|
||||
let info = match docker
|
||||
.inspect_network(&network_name, None::<InspectNetworkOptions<String>>)
|
||||
.await
|
||||
{
|
||||
Ok(info) => info,
|
||||
Err(_) => {
|
||||
log::debug!("Legacy network {} not present, nothing to do", network_name);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(containers) = info.containers {
|
||||
for container_id in containers.into_keys() {
|
||||
let disconnect_opts = bollard::network::DisconnectNetworkOptions {
|
||||
container: container_id.clone(),
|
||||
force: true,
|
||||
};
|
||||
if let Err(e) = docker
|
||||
.disconnect_network(&network_name, disconnect_opts)
|
||||
.await
|
||||
{
|
||||
log::warn!(
|
||||
"Failed to disconnect container {} from legacy network {}: {}",
|
||||
container_id,
|
||||
network_name,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match docker.remove_network(&network_name).await {
|
||||
Ok(_) => log::info!("Removed legacy Docker network {}", network_name),
|
||||
Err(e) => log::warn!("Failed to remove legacy network {}: {}", network_name, e),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user