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",
|
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
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@@ -132,12 +138,12 @@ def get_components_to_install(
|
|||||||
# Explicit components specified
|
# Explicit components specified
|
||||||
if args.components:
|
if args.components:
|
||||||
if "all" in 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:
|
else:
|
||||||
components = args.components
|
components = args.components
|
||||||
|
|
||||||
# If mcp or mcp_docs is specified non-interactively, we should still ask which servers to install.
|
# If mcp or mcp_docs is specified, handle MCP server selection
|
||||||
if "mcp" in components or "mcp_docs" in components:
|
if ("mcp" in components or "mcp_docs" in components) and not args.yes:
|
||||||
selected_servers = select_mcp_servers(registry)
|
selected_servers = select_mcp_servers(registry)
|
||||||
if not hasattr(config_manager, "_installation_context"):
|
if not hasattr(config_manager, "_installation_context"):
|
||||||
config_manager._installation_context = {}
|
config_manager._installation_context = {}
|
||||||
@@ -306,7 +312,7 @@ def select_framework_components(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Framework components (excluding MCP-related ones)
|
# Framework components (excluding MCP-related ones)
|
||||||
framework_components = ["core", "modes", "commands", "agents"]
|
framework_components = ["framework_docs", "modes", "commands", "agents"]
|
||||||
|
|
||||||
# Create component menu
|
# Create component menu
|
||||||
component_options = []
|
component_options = []
|
||||||
@@ -347,9 +353,9 @@ def select_framework_components(
|
|||||||
selections = menu.display()
|
selections = menu.display()
|
||||||
|
|
||||||
if not selections:
|
if not selections:
|
||||||
# Default to core if nothing selected
|
# Default to framework_docs if nothing selected
|
||||||
logger.info("No components selected, defaulting to core")
|
logger.info("No components selected, defaulting to framework_docs")
|
||||||
selected_components = ["core"]
|
selected_components = ["framework_docs"]
|
||||||
else:
|
else:
|
||||||
selected_components = []
|
selected_components = []
|
||||||
all_components = framework_components + ["mcp_docs"]
|
all_components = framework_components + ["mcp_docs"]
|
||||||
@@ -376,7 +382,7 @@ def select_framework_components(
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in framework component selection: {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(
|
def interactive_component_selection(
|
||||||
@@ -564,6 +570,7 @@ def perform_installation(
|
|||||||
"force": args.force,
|
"force": args.force,
|
||||||
"backup": not args.no_backup,
|
"backup": not args.no_backup,
|
||||||
"dry_run": args.dry_run,
|
"dry_run": args.dry_run,
|
||||||
|
"legacy_mode": getattr(args, "legacy", False),
|
||||||
"selected_mcp_servers": getattr(
|
"selected_mcp_servers": getattr(
|
||||||
config_manager, "_installation_context", {}
|
config_manager, "_installation_context", {}
|
||||||
).get("selected_mcp_servers", []),
|
).get("selected_mcp_servers", []),
|
||||||
|
|||||||
@@ -24,7 +24,20 @@ class MCPComponent(Component):
|
|||||||
self.installed_servers_in_session: List[str] = []
|
self.installed_servers_in_session: List[str] = []
|
||||||
|
|
||||||
# Define MCP servers to install
|
# 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": {
|
"sequential-thinking": {
|
||||||
"name": "sequential-thinking",
|
"name": "sequential-thinking",
|
||||||
"description": "Multi-step problem solving and systematic analysis",
|
"description": "Multi-step problem solving and systematic analysis",
|
||||||
@@ -51,54 +64,17 @@ class MCPComponent(Component):
|
|||||||
"npm_package": "@playwright/mcp@latest",
|
"npm_package": "@playwright/mcp@latest",
|
||||||
"required": False,
|
"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]:
|
def get_metadata(self) -> Dict[str, str]:
|
||||||
"""Get component metadata"""
|
"""Get component metadata"""
|
||||||
return {
|
return {
|
||||||
"name": "mcp",
|
"name": "mcp",
|
||||||
"version": __version__,
|
"version": __version__,
|
||||||
"description": "MCP server integration (Context7, Sequential, Magic, Playwright)",
|
"description": "Unified MCP Gateway (airis-mcp-gateway) with all integrated tools",
|
||||||
"category": "integration",
|
"category": "integration",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,33 +112,13 @@ class MCPComponent(Component):
|
|||||||
def validate_prerequisites(
|
def validate_prerequisites(
|
||||||
self, installSubPath: Optional[Path] = None
|
self, installSubPath: Optional[Path] = None
|
||||||
) -> Tuple[bool, List[str]]:
|
) -> Tuple[bool, List[str]]:
|
||||||
"""Check prerequisites"""
|
"""Check prerequisites (varies based on legacy mode)"""
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# Check if Node.js is available
|
# Check which server set we're using
|
||||||
try:
|
is_legacy = self.mcp_servers == self.mcp_servers_legacy
|
||||||
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 version (require 18+)
|
# Check if Claude CLI is available (always required)
|
||||||
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
|
|
||||||
try:
|
try:
|
||||||
result = self._run_command_cross_platform(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "--version"], capture_output=True, text=True, timeout=10
|
["claude", "--version"], capture_output=True, text=True, timeout=10
|
||||||
@@ -177,35 +133,53 @@ class MCPComponent(Component):
|
|||||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||||
errors.append("Claude CLI not found - required for MCP server management")
|
errors.append("Claude CLI not found - required for MCP server management")
|
||||||
|
|
||||||
# Check if npm is available
|
if is_legacy:
|
||||||
try:
|
# Legacy mode: requires Node.js and npm for official servers
|
||||||
result = self._run_command_cross_platform(
|
try:
|
||||||
["npm", "--version"], capture_output=True, text=True, timeout=10
|
result = self._run_command_cross_platform(
|
||||||
)
|
["node", "--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"
|
|
||||||
)
|
)
|
||||||
else:
|
if result.returncode != 0:
|
||||||
version = result.stdout.strip()
|
errors.append("Node.js not found - required for legacy MCP servers")
|
||||||
self.logger.debug(f"Found uv {version}")
|
else:
|
||||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
version = result.stdout.strip()
|
||||||
self.logger.warning(
|
self.logger.debug(f"Found Node.js {version}")
|
||||||
"uv not found - required for Serena MCP server installation"
|
# 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
|
return len(errors) == 0, errors
|
||||||
|
|
||||||
@@ -593,15 +567,9 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
# Map common variations to our standard names
|
# Map common variations to our standard names
|
||||||
name_mappings = {
|
name_mappings = {
|
||||||
"context7": "context7",
|
"airis-mcp-gateway": "airis-mcp-gateway",
|
||||||
"sequential-thinking": "sequential-thinking",
|
"airis": "airis-mcp-gateway",
|
||||||
"sequential": "sequential-thinking",
|
"gateway": "airis-mcp-gateway",
|
||||||
"magic": "magic",
|
|
||||||
"playwright": "playwright",
|
|
||||||
"serena": "serena",
|
|
||||||
"morphllm": "morphllm-fast-apply",
|
|
||||||
"morphllm-fast-apply": "morphllm-fast-apply",
|
|
||||||
"morph": "morphllm-fast-apply",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return name_mappings.get(server_name)
|
return name_mappings.get(server_name)
|
||||||
@@ -798,7 +766,15 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
def _install(self, config: Dict[str, Any]) -> bool:
|
def _install(self, config: Dict[str, Any]) -> bool:
|
||||||
"""Install MCP component with auto-detection of existing servers"""
|
"""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
|
# Validate prerequisites
|
||||||
success, errors = self.validate_prerequisites()
|
success, errors = self.validate_prerequisites()
|
||||||
@@ -965,7 +941,7 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
def get_dependencies(self) -> List[str]:
|
def get_dependencies(self) -> List[str]:
|
||||||
"""Get dependencies"""
|
"""Get dependencies"""
|
||||||
return ["core"]
|
return ["framework_docs"]
|
||||||
|
|
||||||
def update(self, config: Dict[str, Any]) -> bool:
|
def update(self, config: Dict[str, Any]) -> bool:
|
||||||
"""Update MCP component"""
|
"""Update MCP component"""
|
||||||
@@ -1095,9 +1071,21 @@ class MCPComponent(Component):
|
|||||||
return {
|
return {
|
||||||
"component": self.get_metadata()["name"],
|
"component": self.get_metadata()["name"],
|
||||||
"version": self.get_metadata()["version"],
|
"version": self.get_metadata()["version"],
|
||||||
"servers_count": len(self.mcp_servers),
|
"servers_count": 1, # Only airis-mcp-gateway
|
||||||
"mcp_servers": list(self.mcp_servers.keys()),
|
"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(),
|
"estimated_size": self.get_size_estimate(),
|
||||||
"dependencies": self.get_dependencies(),
|
"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
|
# Import and run existing installer logic
|
||||||
# This bridges to the existing setup/cli/commands/install.py implementation
|
# This bridges to the existing setup/cli/commands/install.py implementation
|
||||||
try:
|
try:
|
||||||
@@ -157,8 +147,8 @@ def _run_installation(
|
|||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
verbose=verbose,
|
verbose=verbose,
|
||||||
quiet=False,
|
quiet=False,
|
||||||
yes=non_interactive,
|
yes=True, # Always non-interactive
|
||||||
components=["core", "modes", "commands", "agents", "mcp_docs"], # Full install
|
components=["framework_docs", "modes", "commands", "agents", "mcp_docs"], # Full install
|
||||||
no_backup=False,
|
no_backup=False,
|
||||||
list_components=False,
|
list_components=False,
|
||||||
diagnose=False,
|
diagnose=False,
|
||||||
|
|||||||
Reference in New Issue
Block a user