feat(mcp): add the possibility to have default mcp to connect to

This commit is contained in:
2025-03-25 23:03:50 +01:00
parent 5d674f7508
commit 4b0461a6fa
4 changed files with 206 additions and 21 deletions

View File

@@ -165,6 +165,31 @@ mc config volume remove /local/path
Default volumes will be combined with any volumes specified using the `-v` flag when creating a session.
### Default MCP Servers Configuration
You can configure default MCP servers that sessions will automatically connect to:
```bash
# List default MCP servers
mc config mcp list
# Add an MCP server to defaults
mc config mcp add github
# Remove an MCP server from defaults
mc config mcp remove github
```
When adding new MCP servers, they are added to defaults by default. Use the `--no-default` flag to prevent this:
```bash
# Add an MCP server without adding it to defaults
mc mcp add github ghcr.io/mcp/github:latest --no-default
mc mcp add-remote jira https://jira-mcp.example.com/sse --no-default
```
When creating sessions, if no MCP server is specified with `--mcp`, the default MCP servers will be used automatically.
### External Network Connectivity
MC containers can connect to external Docker networks, allowing them to communicate with other services in those networks:
@@ -269,7 +294,7 @@ mc mcp remote add github http://my-mcp-server.example.com/sse --header "Authoriz
mc mcp docker add github mcp/github:latest --command "github-mcp" --env GITHUB_TOKEN=ghp_123456
# Add a proxy-based MCP server (for stdio-to-SSE conversion)
mc mcp proxy add github ghcr.io/mcp/github:latest --proxy-image ghcr.io/sparfenyuk/mcp-proxy:latest --command "github-mcp" --sse-port 8080
mc mcp add github ghcr.io/mcp/github:latest --proxy-image ghcr.io/sparfenyuk/mcp-proxy:latest --command "github-mcp" --sse-port 8080 --no-default
```
### Using MCP Servers with Sessions

View File

@@ -204,6 +204,15 @@ def create_session(
# Combine default networks with user-specified networks, removing duplicates
all_networks = list(set(default_networks + network))
# Get default MCPs from user config if none specified
all_mcps = mcp if isinstance(mcp, list) else []
if not all_mcps:
default_mcps = user_config.get("defaults.mcps", [])
all_mcps = default_mcps
if default_mcps:
console.print(f"Using default MCP servers: {', '.join(default_mcps)}")
if all_networks:
console.print(f"Networks: {', '.join(all_networks)}")
@@ -222,7 +231,7 @@ def create_session(
mount_local=not no_mount and user_config.get("defaults.mount_local", True),
volumes=volume_mounts,
networks=all_networks,
mcp=mcp,
mcp=all_mcps,
)
if session:
@@ -230,11 +239,6 @@ def create_session(
console.print(f"Session ID: {session.id}")
console.print(f"Driver: {session.driver}")
if session.mcps:
console.print("MCP Servers:")
for mcp in session.mcps:
console.print(f" - {mcp}")
if session.ports:
console.print("Ports:")
for container_port, host_port in session.ports.items():
@@ -392,6 +396,13 @@ def quick_create(
# Use user config for defaults if not specified
if not driver:
driver = user_config.get("defaults.driver")
# Get default MCPs if none specified
all_mcps = mcp if isinstance(mcp, list) else []
if not all_mcps:
default_mcps = user_config.get("defaults.mcps", [])
if default_mcps:
all_mcps = default_mcps
create_session(
driver=driver,
@@ -402,7 +413,7 @@ def quick_create(
name=name,
no_connect=no_connect,
no_mount=no_mount,
mcp=mcp,
mcp=all_mcps,
)
@@ -522,6 +533,64 @@ config_app.add_typer(network_app, name="network", no_args_is_help=True)
volume_app = typer.Typer(help="Manage default volumes")
config_app.add_typer(volume_app, name="volume", no_args_is_help=True)
# Create an MCP subcommand for config
config_mcp_app = typer.Typer(help="Manage default MCP servers")
config_app.add_typer(config_mcp_app, name="mcp", no_args_is_help=True)
# MCP configuration commands
@config_mcp_app.command("list")
def list_default_mcps() -> None:
"""List all default MCP servers"""
default_mcps = user_config.get("defaults.mcps", [])
if not default_mcps:
console.print("No default MCP servers configured")
return
table = Table(show_header=True, header_style="bold")
table.add_column("MCP Server")
for mcp in default_mcps:
table.add_row(mcp)
console.print(table)
@config_mcp_app.command("add")
def add_default_mcp(
name: str = typer.Argument(..., help="MCP server name to add to defaults"),
) -> None:
"""Add an MCP server to default MCPs"""
# First check if the MCP server exists
mcp = mcp_manager.get_mcp(name)
if not mcp:
console.print(f"[red]MCP server '{name}' not found[/red]")
return
default_mcps = user_config.get("defaults.mcps", [])
if name in default_mcps:
console.print(f"MCP server '{name}' is already in defaults")
return
default_mcps.append(name)
user_config.set("defaults.mcps", default_mcps)
console.print(f"[green]Added MCP server '{name}' to defaults[/green]")
@config_mcp_app.command("remove")
def remove_default_mcp(
name: str = typer.Argument(..., help="MCP server name to remove from defaults"),
) -> None:
"""Remove an MCP server from default MCPs"""
default_mcps = user_config.get("defaults.mcps", [])
if name not in default_mcps:
console.print(f"MCP server '{name}' is not in defaults")
return
default_mcps.remove(name)
user_config.set("defaults.mcps", default_mcps)
console.print(f"[green]Removed MCP server '{name}' from defaults[/green]")
# Configuration commands
@config_app.command("list")
@@ -1252,6 +1321,9 @@ def add_mcp(
env: List[str] = typer.Option(
[], "--env", "-e", help="Environment variables (format: KEY=VALUE)"
),
no_default: bool = typer.Option(
False, "--no-default", help="Don't add MCP server to defaults"
),
) -> None:
"""Add a proxy-based MCP server (default type)"""
# Parse environment variables
@@ -1282,6 +1354,7 @@ def add_mcp(
proxy_options,
environment,
host_port,
add_as_default=not no_default,
)
# Get the assigned port
@@ -1292,6 +1365,11 @@ def add_mcp(
console.print(
f"Container port {sse_port} will be bound to host port {assigned_port}"
)
if not no_default:
console.print(f"MCP server '{name}' added to defaults")
else:
console.print(f"MCP server '{name}' not added to defaults")
except Exception as e:
console.print(f"[red]Error adding MCP server: {e}[/red]")
@@ -1304,6 +1382,9 @@ def add_remote_mcp(
header: List[str] = typer.Option(
[], "--header", "-H", help="HTTP headers (format: KEY=VALUE)"
),
no_default: bool = typer.Option(
False, "--no-default", help="Don't add MCP server to defaults"
),
) -> None:
"""Add a remote MCP server"""
# Parse headers
@@ -1319,9 +1400,14 @@ def add_remote_mcp(
try:
with console.status(f"Adding remote MCP server '{name}'..."):
mcp_manager.add_remote_mcp(name, url, headers)
mcp_manager.add_remote_mcp(name, url, headers, add_as_default=not no_default)
console.print(f"[green]Added remote MCP server '{name}'[/green]")
if not no_default:
console.print(f"MCP server '{name}' added to defaults")
else:
console.print(f"MCP server '{name}' not added to defaults")
except Exception as e:
console.print(f"[red]Error adding remote MCP server: {e}[/red]")

View File

@@ -74,9 +74,19 @@ class MCPManager:
return None
def add_remote_mcp(
self, name: str, url: str, headers: Dict[str, str] = None
self, name: str, url: str, headers: Dict[str, str] = None, add_as_default: bool = True
) -> Dict[str, Any]:
"""Add a remote MCP server."""
"""Add a remote MCP server.
Args:
name: Name of the MCP server
url: URL of the remote MCP server
headers: HTTP headers to use when connecting
add_as_default: Whether to add this MCP to the default MCPs list
Returns:
The MCP configuration dictionary
"""
# Create the remote MCP configuration
remote_mcp = RemoteMCP(
name=name,
@@ -91,17 +101,36 @@ class MCPManager:
mcps = [mcp for mcp in mcps if mcp.get("name") != name]
# Add the new MCP
mcps.append(remote_mcp.model_dump())
mcp_config = remote_mcp.model_dump()
mcps.append(mcp_config)
# Save the configuration
self.config_manager.set("mcps", mcps)
# Add to default MCPs if requested
if add_as_default:
default_mcps = self.config_manager.get("defaults.mcps", [])
if name not in default_mcps:
default_mcps.append(name)
self.config_manager.set("defaults.mcps", default_mcps)
return remote_mcp.model_dump()
return mcp_config
def add_docker_mcp(
self, name: str, image: str, command: str, env: Dict[str, str] = None
self, name: str, image: str, command: str, env: Dict[str, str] = None, add_as_default: bool = True
) -> Dict[str, Any]:
"""Add a Docker-based MCP server."""
"""Add a Docker-based MCP server.
Args:
name: Name of the MCP server
image: Docker image for the MCP server
command: Command to run in the container
env: Environment variables to set in the container
add_as_default: Whether to add this MCP to the default MCPs list
Returns:
The MCP configuration dictionary
"""
# Create the Docker MCP configuration
docker_mcp = DockerMCP(
name=name,
@@ -117,12 +146,20 @@ class MCPManager:
mcps = [mcp for mcp in mcps if mcp.get("name") != name]
# Add the new MCP
mcps.append(docker_mcp.model_dump())
mcp_config = docker_mcp.model_dump()
mcps.append(mcp_config)
# Save the configuration
self.config_manager.set("mcps", mcps)
# Add to default MCPs if requested
if add_as_default:
default_mcps = self.config_manager.get("defaults.mcps", [])
if name not in default_mcps:
default_mcps.append(name)
self.config_manager.set("defaults.mcps", default_mcps)
return docker_mcp.model_dump()
return mcp_config
def add_proxy_mcp(
self,
@@ -133,8 +170,23 @@ class MCPManager:
proxy_options: Dict[str, Any] = None,
env: Dict[str, str] = None,
host_port: Optional[int] = None,
add_as_default: bool = True,
) -> Dict[str, Any]:
"""Add a proxy-based MCP server."""
"""Add a proxy-based MCP server.
Args:
name: Name of the MCP server
base_image: Base Docker image running the actual MCP server
proxy_image: Docker image for the MCP proxy
command: Command to run in the container
proxy_options: Options for the MCP proxy
env: Environment variables to set in the container
host_port: Host port to bind the MCP server to (auto-assigned if not specified)
add_as_default: Whether to add this MCP to the default MCPs list
Returns:
The MCP configuration dictionary
"""
# If no host port specified, find the next available port starting from 5101
if host_port is None:
# Get current MCPs and find highest assigned port
@@ -171,15 +223,30 @@ class MCPManager:
mcps = [mcp for mcp in mcps if mcp.get("name") != name]
# Add the new MCP
mcps.append(proxy_mcp.model_dump())
mcp_config = proxy_mcp.model_dump()
mcps.append(mcp_config)
# Save the configuration
self.config_manager.set("mcps", mcps)
# Add to default MCPs if requested
if add_as_default:
default_mcps = self.config_manager.get("defaults.mcps", [])
if name not in default_mcps:
default_mcps.append(name)
self.config_manager.set("defaults.mcps", default_mcps)
return proxy_mcp.model_dump()
return mcp_config
def remove_mcp(self, name: str) -> bool:
"""Remove an MCP server configuration."""
"""Remove an MCP server configuration.
Args:
name: Name of the MCP server to remove
Returns:
True if the MCP was successfully removed, False otherwise
"""
mcps = self.list_mcps()
# Filter out the MCP with the specified name
@@ -191,6 +258,12 @@ class MCPManager:
# Save the updated configuration
self.config_manager.set("mcps", updated_mcps)
# Also remove from default MCPs if it's there
default_mcps = self.config_manager.get("defaults.mcps", [])
if name in default_mcps:
default_mcps.remove(name)
self.config_manager.set("defaults.mcps", default_mcps)
# Stop and remove the container if it exists
self.stop_mcp(name)

View File

@@ -93,6 +93,7 @@ class UserConfigManager:
"mount_local": True,
"networks": [], # Default networks to connect to (besides mc-network)
"volumes": [], # Default volumes to mount, format: "source:dest"
"mcps": [], # Default MCP servers to connect to
},
"services": {
"langfuse": {},