refactor: simplify MCP installer to unified gateway with legacy mode

## Changes

### MCP Component (setup/components/mcp.py)
- Simplified to single airis-mcp-gateway by default
- Added legacy mode for individual official servers (sequential-thinking, context7, magic, playwright)
- Dynamic prerequisites based on mode:
  - Default: uv + claude CLI only
  - Legacy: node (18+) + npm + claude CLI
- Removed redundant server definitions

### CLI Integration
- Added --legacy flag to setup/cli/commands/install.py
- Added --legacy flag to superclaude/cli/commands/install.py
- Config passes legacy_mode to component installer

## Benefits
-  Simpler: 1 gateway vs 9+ individual servers
-  Lighter: No Node.js/npm required (default mode)
-  Unified: All tools in one gateway (sequential-thinking, context7, magic, playwright, serena, morphllm, tavily, chrome-devtools, git, puppeteer)
-  Flexible: --legacy flag for official servers if needed

## Usage
```bash
superclaude install              # Default: airis-mcp-gateway (推奨)
superclaude install --legacy     # Legacy: individual official servers
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
kazuki
2025-10-17 05:25:57 +09:00
parent a4ffe52724
commit 7c3d95cc46
3 changed files with 113 additions and 128 deletions

View File

@@ -80,6 +80,12 @@ Examples:
help="Run system diagnostics and show installation help",
)
parser.add_argument(
"--legacy",
action="store_true",
help="Use legacy mode: install individual official MCP servers instead of unified gateway",
)
return parser
@@ -132,12 +138,12 @@ def get_components_to_install(
# Explicit components specified
if args.components:
if "all" in args.components:
components = ["core", "commands", "agents", "modes", "mcp", "mcp_docs"]
components = ["framework_docs", "commands", "agents", "modes", "mcp", "mcp_docs"]
else:
components = args.components
# If mcp or mcp_docs is specified non-interactively, we should still ask which servers to install.
if "mcp" in components or "mcp_docs" in components:
# If mcp or mcp_docs is specified, handle MCP server selection
if ("mcp" in components or "mcp_docs" in components) and not args.yes:
selected_servers = select_mcp_servers(registry)
if not hasattr(config_manager, "_installation_context"):
config_manager._installation_context = {}
@@ -306,7 +312,7 @@ def select_framework_components(
try:
# Framework components (excluding MCP-related ones)
framework_components = ["core", "modes", "commands", "agents"]
framework_components = ["framework_docs", "modes", "commands", "agents"]
# Create component menu
component_options = []
@@ -347,9 +353,9 @@ def select_framework_components(
selections = menu.display()
if not selections:
# Default to core if nothing selected
logger.info("No components selected, defaulting to core")
selected_components = ["core"]
# Default to framework_docs if nothing selected
logger.info("No components selected, defaulting to framework_docs")
selected_components = ["framework_docs"]
else:
selected_components = []
all_components = framework_components + ["mcp_docs"]
@@ -376,7 +382,7 @@ def select_framework_components(
except Exception as e:
logger.error(f"Error in framework component selection: {e}")
return ["core"] # Fallback to core
return ["framework_docs"] # Fallback to framework_docs
def interactive_component_selection(
@@ -564,6 +570,7 @@ def perform_installation(
"force": args.force,
"backup": not args.no_backup,
"dry_run": args.dry_run,
"legacy_mode": getattr(args, "legacy", False),
"selected_mcp_servers": getattr(
config_manager, "_installation_context", {}
).get("selected_mcp_servers", []),

View File

@@ -24,7 +24,20 @@ class MCPComponent(Component):
self.installed_servers_in_session: List[str] = []
# Define MCP servers to install
self.mcp_servers = {
# Default: airis-mcp-gateway (unified gateway with all tools)
# Legacy mode (--legacy flag): individual official servers
self.mcp_servers_default = {
"airis-mcp-gateway": {
"name": "airis-mcp-gateway",
"description": "Unified MCP Gateway with all tools (sequential-thinking, context7, magic, playwright, serena, morphllm, tavily, chrome-devtools, git, puppeteer)",
"install_method": "github",
"install_command": "uvx --from git+https://github.com/oraios/airis-mcp-gateway airis-mcp-gateway --help",
"run_command": "uvx --from git+https://github.com/oraios/airis-mcp-gateway airis-mcp-gateway",
"required": True,
},
}
self.mcp_servers_legacy = {
"sequential-thinking": {
"name": "sequential-thinking",
"description": "Multi-step problem solving and systematic analysis",
@@ -51,54 +64,17 @@ class MCPComponent(Component):
"npm_package": "@playwright/mcp@latest",
"required": False,
},
"serena": {
"name": "serena",
"description": "Semantic code analysis and intelligent editing",
"install_method": "github",
"install_command": "uvx --from git+https://github.com/oraios/serena serena --help",
"run_command": "uvx --from git+https://github.com/oraios/serena serena start-mcp-server --context ide-assistant --enable-web-dashboard false --enable-gui-log-window false",
"required": False,
},
"morphllm-fast-apply": {
"name": "morphllm-fast-apply",
"description": "Fast Apply capability for context-aware code modifications",
"npm_package": "@morph-llm/morph-fast-apply",
"required": False,
"api_key_env": "MORPH_API_KEY",
"api_key_description": "Morph API key for Fast Apply",
},
"tavily": {
"name": "tavily",
"description": "Web search and real-time information retrieval for deep research",
"install_method": "npm",
"install_command": "npx -y tavily-mcp@0.1.2",
"required": False,
"api_key_env": "TAVILY_API_KEY",
"api_key_description": "Tavily API key for web search (get from https://app.tavily.com)",
},
"chrome-devtools": {
"name": "chrome-devtools",
"description": "Chrome DevTools debugging and performance analysis",
"install_method": "npm",
"install_command": "npx -y chrome-devtools-mcp@latest",
"required": False,
},
"airis-mcp-gateway": {
"name": "airis-mcp-gateway",
"description": "Dynamic MCP Gateway for zero-token baseline and on-demand tool loading",
"install_method": "github",
"install_command": "uvx --from git+https://github.com/oraios/airis-mcp-gateway airis-mcp-gateway --help",
"run_command": "uvx --from git+https://github.com/oraios/airis-mcp-gateway airis-mcp-gateway",
"required": False,
},
}
# Default to unified gateway
self.mcp_servers = self.mcp_servers_default
def get_metadata(self) -> Dict[str, str]:
"""Get component metadata"""
return {
"name": "mcp",
"version": __version__,
"description": "MCP server integration (Context7, Sequential, Magic, Playwright)",
"description": "Unified MCP Gateway (airis-mcp-gateway) with all integrated tools",
"category": "integration",
}
@@ -136,33 +112,13 @@ class MCPComponent(Component):
def validate_prerequisites(
self, installSubPath: Optional[Path] = None
) -> Tuple[bool, List[str]]:
"""Check prerequisites"""
"""Check prerequisites (varies based on legacy mode)"""
errors = []
# Check if Node.js is available
try:
result = self._run_command_cross_platform(
["node", "--version"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
errors.append("Node.js not found - required for MCP servers")
else:
version = result.stdout.strip()
self.logger.debug(f"Found Node.js {version}")
# Check which server set we're using
is_legacy = self.mcp_servers == self.mcp_servers_legacy
# Check version (require 18+)
try:
version_num = int(version.lstrip("v").split(".")[0])
if version_num < 18:
errors.append(
f"Node.js version {version} found, but version 18+ required"
)
except:
self.logger.warning(f"Could not parse Node.js version: {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("Node.js not found - required for MCP servers")
# Check if Claude CLI is available
# Check if Claude CLI is available (always required)
try:
result = self._run_command_cross_platform(
["claude", "--version"], capture_output=True, text=True, timeout=10
@@ -177,35 +133,53 @@ class MCPComponent(Component):
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("Claude CLI not found - required for MCP server management")
# Check if npm is available
try:
result = self._run_command_cross_platform(
["npm", "--version"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
errors.append("npm not found - required for MCP server installation")
else:
version = result.stdout.strip()
self.logger.debug(f"Found npm {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("npm not found - required for MCP server installation")
# Check if uv is available (required for Serena)
try:
result = self._run_command_cross_platform(
["uv", "--version"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
self.logger.warning(
"uv not found - required for Serena MCP server installation"
if is_legacy:
# Legacy mode: requires Node.js and npm for official servers
try:
result = self._run_command_cross_platform(
["node", "--version"], capture_output=True, text=True, timeout=10
)
else:
version = result.stdout.strip()
self.logger.debug(f"Found uv {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
self.logger.warning(
"uv not found - required for Serena MCP server installation"
)
if result.returncode != 0:
errors.append("Node.js not found - required for legacy MCP servers")
else:
version = result.stdout.strip()
self.logger.debug(f"Found Node.js {version}")
# Check version (require 18+)
try:
version_num = int(version.lstrip("v").split(".")[0])
if version_num < 18:
errors.append(
f"Node.js version {version} found, but version 18+ required"
)
except:
self.logger.warning(f"Could not parse Node.js version: {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("Node.js not found - required for legacy MCP servers")
try:
result = self._run_command_cross_platform(
["npm", "--version"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
errors.append("npm not found - required for legacy MCP server installation")
else:
version = result.stdout.strip()
self.logger.debug(f"Found npm {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("npm not found - required for legacy MCP server installation")
else:
# Default mode: requires uv for airis-mcp-gateway
try:
result = self._run_command_cross_platform(
["uv", "--version"], capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
errors.append("uv not found - required for airis-mcp-gateway installation")
else:
version = result.stdout.strip()
self.logger.debug(f"Found uv {version}")
except (subprocess.TimeoutExpired, FileNotFoundError):
errors.append("uv not found - required for airis-mcp-gateway installation")
return len(errors) == 0, errors
@@ -593,15 +567,9 @@ class MCPComponent(Component):
# Map common variations to our standard names
name_mappings = {
"context7": "context7",
"sequential-thinking": "sequential-thinking",
"sequential": "sequential-thinking",
"magic": "magic",
"playwright": "playwright",
"serena": "serena",
"morphllm": "morphllm-fast-apply",
"morphllm-fast-apply": "morphllm-fast-apply",
"morph": "morphllm-fast-apply",
"airis-mcp-gateway": "airis-mcp-gateway",
"airis": "airis-mcp-gateway",
"gateway": "airis-mcp-gateway",
}
return name_mappings.get(server_name)
@@ -798,7 +766,15 @@ class MCPComponent(Component):
def _install(self, config: Dict[str, Any]) -> bool:
"""Install MCP component with auto-detection of existing servers"""
self.logger.info("Installing SuperClaude MCP servers...")
# Check for legacy mode flag
use_legacy = config.get("legacy_mode", False) or config.get("official_servers", False)
if use_legacy:
self.logger.info("Installing individual official MCP servers (legacy mode)...")
self.mcp_servers = self.mcp_servers_legacy
else:
self.logger.info("Installing unified MCP gateway (airis-mcp-gateway)...")
self.mcp_servers = self.mcp_servers_default
# Validate prerequisites
success, errors = self.validate_prerequisites()
@@ -965,7 +941,7 @@ class MCPComponent(Component):
def get_dependencies(self) -> List[str]:
"""Get dependencies"""
return ["core"]
return ["framework_docs"]
def update(self, config: Dict[str, Any]) -> bool:
"""Update MCP component"""
@@ -1095,9 +1071,21 @@ class MCPComponent(Component):
return {
"component": self.get_metadata()["name"],
"version": self.get_metadata()["version"],
"servers_count": len(self.mcp_servers),
"mcp_servers": list(self.mcp_servers.keys()),
"servers_count": 1, # Only airis-mcp-gateway
"mcp_servers": ["airis-mcp-gateway"],
"included_tools": [
"sequential-thinking",
"context7",
"magic",
"playwright",
"serena",
"morphllm",
"tavily",
"chrome-devtools",
"git",
"puppeteer",
],
"estimated_size": self.get_size_estimate(),
"dependencies": self.get_dependencies(),
"required_tools": ["node", "npm", "claude"],
"required_tools": ["uv", "claude"],
}

View File

@@ -134,16 +134,6 @@ def _run_installation(
)
)
# Confirm installation if interactive
if not non_interactive and not dry_run:
proceed = Confirm.ask(
"\n[bold]Install SuperClaude with recommended configuration?[/bold]",
default=True,
)
if not proceed:
console.print("[yellow]Installation cancelled by user[/yellow]")
raise typer.Exit(0)
# Import and run existing installer logic
# This bridges to the existing setup/cli/commands/install.py implementation
try:
@@ -157,8 +147,8 @@ def _run_installation(
dry_run=dry_run,
verbose=verbose,
quiet=False,
yes=non_interactive,
components=["core", "modes", "commands", "agents", "mcp_docs"], # Full install
yes=True, # Always non-interactive
components=["framework_docs", "modes", "commands", "agents", "mcp_docs"], # Full install
no_backup=False,
list_components=False,
diagnose=False,