Skip to content

Commit 06901e8

Browse files
committed
feat(core): Implement Auto-UVX Injection for Python servers
- Upgrades execution to 'uv run' if uv is detected - Solves dependency isolation and Pydantic version conflicts - Eliminates need for pip install step for Python servers
1 parent c457d7e commit 06901e8

1 file changed

Lines changed: 39 additions & 4 deletions

File tree

src/mcpm/commands/install.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import os
77
import re
8+
import shutil
89
from enum import Enum
910

1011
from prompt_toolkit import PromptSession
@@ -95,9 +96,7 @@ def prompt_with_default(prompt_text, default="", hide_input=False, required=Fals
9596
return default
9697
if required:
9798
# Cannot fulfill required argument without default in non-interactive mode
98-
raise click.UsageError(
99-
"A required value has no default and cannot be prompted in non-interactive mode."
100-
)
99+
raise click.UsageError("A required value has no default and cannot be prompted in non-interactive mode.")
101100
return ""
102101

103102
# if default:
@@ -178,7 +177,9 @@ def install(server_name, force=False, alias=None):
178177

179178
# Confirm addition
180179
alias_text = f" as '{alias}'" if alias else ""
181-
if not should_force_operation(force) and not Confirm.ask(f"Install this server to global configuration{alias_text}?"):
180+
if not should_force_operation(force) and not Confirm.ask(
181+
f"Install this server to global configuration{alias_text}?"
182+
):
182183
console.print("[yellow]Operation cancelled.[/]")
183184
return
184185

@@ -428,6 +429,40 @@ def install(server_name, force=False, alias=None):
428429
mcp_command = install_command
429430
mcp_args = processed_args
430431

432+
# --- Auto-UVX Injection Logic ---
433+
# If 'uv' is available and we are using python/pip, try to upgrade to 'uv run' for isolation.
434+
# This solves the "Pydantic Versioning" dependency hell by isolating servers.
435+
if mcp_command in ["python", "python3", "pip"] and shutil.which("uv"):
436+
# We need to determine the package name to run 'uv run --with <package>'
437+
# If package_name was defined in the installation method, use it.
438+
# If not, check if we are running 'python -m <module>' and guess package name from module?
439+
# Or default to the server name if reasonable?
440+
target_package = package_name
441+
442+
# If args start with '-m', the next arg is the module.
443+
# Often module == package (e.g. mcp_server_time -> mcp-server-time? No, dashes vs underscores).
444+
# But 'uv run --with <module> python -m <module>' usually works if PyPI name matches.
445+
446+
if not target_package and mcp_args and mcp_args[0] == "-m" and len(mcp_args) > 1:
447+
# Heuristic: Assume package name matches module name (with _ -> - maybe?)
448+
# Ideally, the registry should provide 'package'.
449+
# For now, we only auto-upgrade if we have a package name OR if we are brave.
450+
# Let's rely on package_name variable extracted earlier from selected_method.get("package")
451+
pass
452+
453+
if target_package:
454+
console.print(f"[bold blue]🚀 Auto-upgrading to 'uv run' for isolation (package: {target_package})[/]")
455+
# Old: python -m module ...
456+
# New: uv run --with package python -m module ...
457+
458+
# We prepend 'run --with package' to the command execution
459+
# mcp_command becomes 'uv'
460+
# mcp_args becomes ['run', '--with', target_package, original_command] + mcp_args
461+
462+
new_args = ["run", "--with", target_package, mcp_command] + mcp_args
463+
mcp_command = "uv"
464+
mcp_args = new_args
465+
431466
# Create server configuration using FullServerConfig
432467
full_server_config = FullServerConfig(
433468
name=config_name,

0 commit comments

Comments
 (0)