mirror of
https://github.com/Monadical-SAS/cubbi.git
synced 2025-12-20 12:19:07 +00:00
feat(mcp): add the possibility to have default mcp to connect to
This commit is contained in:
27
README.md
27
README.md
@@ -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
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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": {},
|
||||
|
||||
Reference in New Issue
Block a user