mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
fix: mcp cross-platform shell handling and rename morphllm (#369)
fix: mcp cross-platform shell handling and rename morphllm to morphllm-fast-apply
This commit is contained in:
@@ -2,15 +2,19 @@
|
|||||||
MCP component for MCP server integration
|
MCP component for MCP server integration
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, List, Tuple, Optional, Any
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shlex
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
from setup import __version__
|
||||||
|
|
||||||
from ..core.base import Component
|
from ..core.base import Component
|
||||||
from ..utils.ui import display_info, display_warning
|
from ..utils.ui import display_info, display_warning
|
||||||
from setup import __version__
|
|
||||||
|
|
||||||
class MCPComponent(Component):
|
class MCPComponent(Component):
|
||||||
"""MCP servers integration component"""
|
"""MCP servers integration component"""
|
||||||
@@ -56,7 +60,7 @@ class MCPComponent(Component):
|
|||||||
"run_command": "serena start-mcp-server --context ide-assistant --project $(pwd)",
|
"run_command": "serena start-mcp-server --context ide-assistant --project $(pwd)",
|
||||||
"required": False
|
"required": False
|
||||||
},
|
},
|
||||||
"morphllm": {
|
"morphllm-fast-apply": {
|
||||||
"name": "morphllm-fast-apply",
|
"name": "morphllm-fast-apply",
|
||||||
"description": "Fast Apply capability for context-aware code modifications",
|
"description": "Fast Apply capability for context-aware code modifications",
|
||||||
"npm_package": "@morph-llm/morph-fast-apply",
|
"npm_package": "@morph-llm/morph-fast-apply",
|
||||||
@@ -79,18 +83,42 @@ class MCPComponent(Component):
|
|||||||
"""This component manages sub-components (servers) and should be re-run."""
|
"""This component manages sub-components (servers) and should be re-run."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _run_command_cross_platform(self, cmd: List[str], **kwargs) -> subprocess.CompletedProcess:
|
||||||
|
"""
|
||||||
|
Run a command with proper cross-platform shell handling.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cmd: Command as list of strings
|
||||||
|
**kwargs: Additional subprocess.run arguments
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
CompletedProcess result
|
||||||
|
"""
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
# Windows: Use list format with shell=True
|
||||||
|
return subprocess.run(cmd, shell=True, **kwargs)
|
||||||
|
else:
|
||||||
|
# macOS/Linux: Use string format with proper shell to support aliases
|
||||||
|
cmd_str = " ".join(shlex.quote(str(arg)) for arg in cmd)
|
||||||
|
|
||||||
|
# Use the user's shell with interactive mode to load aliases
|
||||||
|
user_shell = os.environ.get('SHELL', '/bin/bash')
|
||||||
|
|
||||||
|
# Execute command with user's shell in interactive mode to load aliases
|
||||||
|
full_cmd = f"{user_shell} -i -c {shlex.quote(cmd_str)}"
|
||||||
|
return subprocess.run(full_cmd, shell=True, env=os.environ, **kwargs)
|
||||||
|
|
||||||
def validate_prerequisites(self, installSubPath: Optional[Path] = None) -> Tuple[bool, List[str]]:
|
def validate_prerequisites(self, installSubPath: Optional[Path] = None) -> Tuple[bool, List[str]]:
|
||||||
"""Check prerequisites"""
|
"""Check prerequisites"""
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# Check if Node.js is available
|
# Check if Node.js is available
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["node", "--version"],
|
["node", "--version"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=10,
|
timeout=10
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
errors.append("Node.js not found - required for MCP servers")
|
errors.append("Node.js not found - required for MCP servers")
|
||||||
@@ -110,12 +138,11 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
# Check if Claude CLI is available
|
# Check if Claude CLI is available
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "--version"],
|
["claude", "--version"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=10,
|
timeout=10
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
errors.append("Claude CLI not found - required for MCP server management")
|
errors.append("Claude CLI not found - required for MCP server management")
|
||||||
@@ -127,12 +154,11 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
# Check if npm is available
|
# Check if npm is available
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["npm", "--version"],
|
["npm", "--version"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=10,
|
timeout=10
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
errors.append("npm not found - required for MCP server installation")
|
errors.append("npm not found - required for MCP server installation")
|
||||||
@@ -193,24 +219,22 @@ class MCPComponent(Component):
|
|||||||
# Run install command
|
# Run install command
|
||||||
self.logger.debug(f"Running: {install_command}")
|
self.logger.debug(f"Running: {install_command}")
|
||||||
cmd_parts = shlex.split(install_command)
|
cmd_parts = shlex.split(install_command)
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
cmd_parts,
|
cmd_parts,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=900, # 15 minutes
|
timeout=900 # 15 minutes
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
self.logger.success(f"Successfully installed MCP server (user scope): {server_name}")
|
self.logger.success(f"Successfully installed MCP server (user scope): {server_name}")
|
||||||
|
|
||||||
self.logger.info(f"Registering {server_name} with Claude CLI. Run command: {run_command}")
|
self.logger.info(f"Registering {server_name} with Claude CLI. Run command: {run_command}")
|
||||||
reg_result = subprocess.run(
|
reg_result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "add", "-s", "user", "--", server_name] + shlex.split(run_command),
|
["claude", "mcp", "add", "-s", "user", "--", server_name] + shlex.split(run_command),
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=120,
|
timeout=120
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if reg_result.returncode == 0:
|
if reg_result.returncode == 0:
|
||||||
@@ -235,12 +259,11 @@ class MCPComponent(Component):
|
|||||||
def _check_mcp_server_installed(self, server_name: str) -> bool:
|
def _check_mcp_server_installed(self, server_name: str) -> bool:
|
||||||
"""Check if MCP server is already installed"""
|
"""Check if MCP server is already installed"""
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "list"],
|
["claude", "mcp", "list"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=60,
|
timeout=60
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
@@ -300,12 +323,11 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
self.logger.debug(f"Running: claude mcp add -s user {server_name} {command} -y {npm_package}")
|
self.logger.debug(f"Running: claude mcp add -s user {server_name} {command} -y {npm_package}")
|
||||||
|
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "add", "-s", "user", "--", server_name, command, "-y", npm_package],
|
["claude", "mcp", "add", "-s", "user", "--", server_name, command, "-y", npm_package],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=120, # 2 minutes timeout for installation
|
timeout=120 # 2 minutes timeout for installation
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
@@ -335,12 +357,11 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
self.logger.debug(f"Running: claude mcp remove {server_name} (auto-detect scope)")
|
self.logger.debug(f"Running: claude mcp remove {server_name} (auto-detect scope)")
|
||||||
|
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "remove", server_name],
|
["claude", "mcp", "remove", server_name],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=60,
|
timeout=60
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
@@ -403,12 +424,11 @@ class MCPComponent(Component):
|
|||||||
if not config.get("dry_run", False):
|
if not config.get("dry_run", False):
|
||||||
self.logger.info("Verifying MCP server installation...")
|
self.logger.info("Verifying MCP server installation...")
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "list"],
|
["claude", "mcp", "list"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=60,
|
timeout=60
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
@@ -562,12 +582,11 @@ class MCPComponent(Component):
|
|||||||
|
|
||||||
# Check if Claude CLI is available and validate installed servers
|
# Check if Claude CLI is available and validate installed servers
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = self._run_command_cross_platform(
|
||||||
["claude", "mcp", "list"],
|
["claude", "mcp", "list"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=60,
|
timeout=60
|
||||||
shell=(sys.platform == "win32")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user