mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
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:
@@ -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", []),
|
||||
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user