rename project: esxi-mcp-server → mcvsphere
mcvsphere = Model Control for vSphere Updates: - Package renamed from esxi_mcp_server to mcvsphere - CLI entry point: mcvsphere (was esxi-mcp-server) - All imports and references updated - Docker configs updated - Test suites updated
This commit is contained in:
parent
c65f91c571
commit
eb59cd5e9a
@ -1,4 +1,4 @@
|
|||||||
# ESXi MCP Server - Modern Python with uv
|
# mcvsphere - Modern Python with uv
|
||||||
# Multi-stage build for optimal image size
|
# Multi-stage build for optimal image size
|
||||||
|
|
||||||
# Build stage
|
# Build stage
|
||||||
@ -63,5 +63,5 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
|||||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080')" || exit 1
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080')" || exit 1
|
||||||
|
|
||||||
# Run the MCP server
|
# Run the MCP server
|
||||||
ENTRYPOINT ["esxi-mcp-server"]
|
ENTRYPOINT ["mcvsphere"]
|
||||||
CMD ["--transport", "sse"]
|
CMD ["--transport", "sse"]
|
||||||
|
|||||||
26
README.md
26
README.md
@ -1,10 +1,10 @@
|
|||||||
# ESXi MCP Server
|
# mcvsphere
|
||||||
|
|
||||||
A comprehensive VMware vSphere management server implementing the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), enabling AI assistants like Claude to manage virtual infrastructure through natural language.
|
A comprehensive VMware vSphere management server implementing the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), enabling AI assistants like Claude to manage virtual infrastructure through natural language.
|
||||||
|
|
||||||
## Why ESXi MCP Server?
|
## Why mcvsphere?
|
||||||
|
|
||||||
Traditional VMware management requires navigating complex UIs or writing scripts. With ESXi MCP Server, you can simply ask:
|
Traditional VMware management requires navigating complex UIs or writing scripts. With mcvsphere, you can simply ask:
|
||||||
|
|
||||||
> "Create a new VM with 4 CPUs and 8GB RAM, then take a snapshot before installing the OS"
|
> "Create a new VM with 4 CPUs and 8GB RAM, then take a snapshot before installing the OS"
|
||||||
|
|
||||||
@ -26,10 +26,10 @@ And watch it happen. The server exposes **94 tools** covering every aspect of vS
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install with uv (recommended)
|
# Install with uv (recommended)
|
||||||
uvx esxi-mcp-server
|
uvx mcvsphere
|
||||||
|
|
||||||
# Or install with pip
|
# Or install with pip
|
||||||
pip install esxi-mcp-server
|
pip install mcvsphere
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
@ -47,16 +47,16 @@ VCENTER_INSECURE=true # Skip SSL verification (dev only)
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Using uvx
|
# Using uvx
|
||||||
uvx esxi-mcp-server
|
uvx mcvsphere
|
||||||
|
|
||||||
# Or if installed
|
# Or if installed
|
||||||
esxi-mcp-server
|
mcvsphere
|
||||||
```
|
```
|
||||||
|
|
||||||
### Add to Claude Code
|
### Add to Claude Code
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
claude mcp add esxi "uvx esxi-mcp-server"
|
claude mcp add esxi "uvx mcvsphere"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Available Tools (94 Total)
|
## Available Tools (94 Total)
|
||||||
@ -220,7 +220,7 @@ Access real-time data through MCP resources:
|
|||||||
The server uses a modular mixin architecture:
|
The server uses a modular mixin architecture:
|
||||||
|
|
||||||
```
|
```
|
||||||
esxi_mcp_server/
|
mcvsphere/
|
||||||
├── server.py # FastMCP server setup
|
├── server.py # FastMCP server setup
|
||||||
├── connection.py # VMware connection management
|
├── connection.py # VMware connection management
|
||||||
├── config.py # Settings and configuration
|
├── config.py # Settings and configuration
|
||||||
@ -259,14 +259,14 @@ esxi_mcp_server/
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build
|
# Build
|
||||||
docker build -t esxi-mcp-server .
|
docker build -t mcvsphere .
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
docker run -d \
|
docker run -d \
|
||||||
-e VCENTER_HOST=vcenter.example.com \
|
-e VCENTER_HOST=vcenter.example.com \
|
||||||
-e VCENTER_USER=admin@vsphere.local \
|
-e VCENTER_USER=admin@vsphere.local \
|
||||||
-e VCENTER_PASSWORD=secret \
|
-e VCENTER_PASSWORD=secret \
|
||||||
esxi-mcp-server
|
mcvsphere
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@ -320,8 +320,8 @@ telnet://10.20.0.22:4521
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repo
|
# Clone the repo
|
||||||
git clone https://github.com/yourusername/esxi-mcp-server.git
|
git clone https://github.com/yourusername/mcvsphere.git
|
||||||
cd esxi-mcp-server
|
cd mcvsphere
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
uv sync
|
uv sync
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# ESXi MCP Server - Docker Guide
|
# mcvsphere - Docker Guide
|
||||||
|
|
||||||
This guide provides instructions for running the ESXi MCP Server using Docker and Docker Compose.
|
This guide provides instructions for running the mcvsphere using Docker and Docker Compose.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ This guide provides instructions for running the ESXi MCP Server using Docker an
|
|||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone <repository-url>
|
git clone <repository-url>
|
||||||
cd esxi-mcp-server
|
cd mcvsphere
|
||||||
|
|
||||||
# Create necessary directories and configuration
|
# Create necessary directories and configuration
|
||||||
make setup
|
make setup
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
# Supports dev (hot-reload) and prod modes via COMPOSE_PROFILES
|
# Supports dev (hot-reload) and prod modes via COMPOSE_PROFILES
|
||||||
|
|
||||||
services:
|
services:
|
||||||
esxi-mcp-server:
|
mcvsphere:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: esxi-mcp-server
|
container_name: mcvsphere
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
profiles: ["prod"]
|
profiles: ["prod"]
|
||||||
ports:
|
ports:
|
||||||
@ -37,11 +37,11 @@ services:
|
|||||||
cpus: '0.25'
|
cpus: '0.25'
|
||||||
|
|
||||||
# Development mode with hot-reload
|
# Development mode with hot-reload
|
||||||
esxi-mcp-server-dev:
|
mcvsphere-dev:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.dev
|
dockerfile: Dockerfile.dev
|
||||||
container_name: esxi-mcp-server-dev
|
container_name: mcvsphere-dev
|
||||||
profiles: ["dev"]
|
profiles: ["dev"]
|
||||||
ports:
|
ports:
|
||||||
- "${MCP_PORT:-8080}:8080"
|
- "${MCP_PORT:-8080}:8080"
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "esxi-mcp-server"
|
name = "mcvsphere"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
description = "VMware ESXi/vCenter MCP server for AI-driven virtual machine management"
|
description = "Model Control for vSphere - AI-driven VMware virtual machine management via MCP"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Ryan Malloy", email = "ryan@supported.systems"},
|
{name = "Ryan Malloy", email = "ryan@supported.systems"},
|
||||||
]
|
]
|
||||||
keywords = ["mcp", "vmware", "esxi", "vcenter", "fastmcp", "virtualization"]
|
keywords = ["mcp", "vmware", "esxi", "vcenter", "vsphere", "fastmcp", "virtualization", "model-context-protocol"]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
@ -36,18 +36,18 @@ dev = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
esxi-mcp-server = "esxi_mcp_server:main"
|
mcvsphere = "mcvsphere:main"
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/Bright8192/esxi-mcp-server"
|
Homepage = "https://git.supported.systems/MCP/mcvsphere"
|
||||||
Repository = "https://github.com/Bright8192/esxi-mcp-server"
|
Repository = "https://git.supported.systems/MCP/mcvsphere"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/esxi_mcp_server"]
|
packages = ["src/mcvsphere"]
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py311"
|
target-version = "py311"
|
||||||
@ -72,7 +72,7 @@ ignore = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
known-first-party = ["esxi_mcp_server"]
|
known-first-party = ["mcvsphere"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
"""MCP Mixins for ESXi operations organized by category."""
|
|
||||||
|
|
||||||
from esxi_mcp_server.mixins.console import ConsoleMixin
|
|
||||||
from esxi_mcp_server.mixins.disk_management import DiskManagementMixin
|
|
||||||
from esxi_mcp_server.mixins.guest_ops import GuestOpsMixin
|
|
||||||
from esxi_mcp_server.mixins.host_management import HostManagementMixin
|
|
||||||
from esxi_mcp_server.mixins.monitoring import MonitoringMixin
|
|
||||||
from esxi_mcp_server.mixins.nic_management import NICManagementMixin
|
|
||||||
from esxi_mcp_server.mixins.ovf_management import OVFManagementMixin
|
|
||||||
from esxi_mcp_server.mixins.power_ops import PowerOpsMixin
|
|
||||||
from esxi_mcp_server.mixins.resources import ResourcesMixin
|
|
||||||
from esxi_mcp_server.mixins.serial_port import SerialPortMixin
|
|
||||||
from esxi_mcp_server.mixins.snapshots import SnapshotsMixin
|
|
||||||
from esxi_mcp_server.mixins.vcenter_ops import VCenterOpsMixin
|
|
||||||
from esxi_mcp_server.mixins.vm_lifecycle import VMLifecycleMixin
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"ConsoleMixin",
|
|
||||||
"DiskManagementMixin",
|
|
||||||
"GuestOpsMixin",
|
|
||||||
"HostManagementMixin",
|
|
||||||
"MonitoringMixin",
|
|
||||||
"NICManagementMixin",
|
|
||||||
"OVFManagementMixin",
|
|
||||||
"PowerOpsMixin",
|
|
||||||
"ResourcesMixin",
|
|
||||||
"SerialPortMixin",
|
|
||||||
"SnapshotsMixin",
|
|
||||||
"VCenterOpsMixin",
|
|
||||||
"VMLifecycleMixin",
|
|
||||||
]
|
|
||||||
@ -7,9 +7,9 @@ virtual machines through AI assistants like Claude.
|
|||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from esxi_mcp_server.config import Settings, get_settings
|
from mcvsphere.config import Settings, get_settings
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
from esxi_mcp_server.server import create_server, run_server
|
from mcvsphere.server import create_server, run_server
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Settings",
|
"Settings",
|
||||||
@ -22,7 +22,7 @@ __all__ = [
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Entry point for the esxi-mcp-server CLI."""
|
"""Entry point for the mcvsphere CLI."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="ESXi MCP Server - VMware vSphere management via MCP"
|
description="ESXi MCP Server - VMware vSphere management via MCP"
|
||||||
)
|
)
|
||||||
@ -8,7 +8,7 @@ from pyVim import connect
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.config import Settings
|
from mcvsphere.config import Settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
31
src/mcvsphere/mixins/__init__.py
Normal file
31
src/mcvsphere/mixins/__init__.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""MCP Mixins for ESXi operations organized by category."""
|
||||||
|
|
||||||
|
from mcvsphere.mixins.console import ConsoleMixin
|
||||||
|
from mcvsphere.mixins.disk_management import DiskManagementMixin
|
||||||
|
from mcvsphere.mixins.guest_ops import GuestOpsMixin
|
||||||
|
from mcvsphere.mixins.host_management import HostManagementMixin
|
||||||
|
from mcvsphere.mixins.monitoring import MonitoringMixin
|
||||||
|
from mcvsphere.mixins.nic_management import NICManagementMixin
|
||||||
|
from mcvsphere.mixins.ovf_management import OVFManagementMixin
|
||||||
|
from mcvsphere.mixins.power_ops import PowerOpsMixin
|
||||||
|
from mcvsphere.mixins.resources import ResourcesMixin
|
||||||
|
from mcvsphere.mixins.serial_port import SerialPortMixin
|
||||||
|
from mcvsphere.mixins.snapshots import SnapshotsMixin
|
||||||
|
from mcvsphere.mixins.vcenter_ops import VCenterOpsMixin
|
||||||
|
from mcvsphere.mixins.vm_lifecycle import VMLifecycleMixin
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ConsoleMixin",
|
||||||
|
"DiskManagementMixin",
|
||||||
|
"GuestOpsMixin",
|
||||||
|
"HostManagementMixin",
|
||||||
|
"MonitoringMixin",
|
||||||
|
"NICManagementMixin",
|
||||||
|
"OVFManagementMixin",
|
||||||
|
"PowerOpsMixin",
|
||||||
|
"ResourcesMixin",
|
||||||
|
"SerialPortMixin",
|
||||||
|
"SnapshotsMixin",
|
||||||
|
"VCenterOpsMixin",
|
||||||
|
"VMLifecycleMixin",
|
||||||
|
]
|
||||||
@ -11,7 +11,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class ConsoleMixin(MCPMixin):
|
class ConsoleMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class DiskManagementMixin(MCPMixin):
|
class DiskManagementMixin(MCPMixin):
|
||||||
@ -9,7 +9,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class GuestOpsMixin(MCPMixin):
|
class GuestOpsMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class HostManagementMixin(MCPMixin):
|
class HostManagementMixin(MCPMixin):
|
||||||
@ -8,7 +8,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class MonitoringMixin(MCPMixin):
|
class MonitoringMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class NICManagementMixin(MCPMixin):
|
class NICManagementMixin(MCPMixin):
|
||||||
@ -12,7 +12,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class OVFManagementMixin(MCPMixin):
|
class OVFManagementMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class PowerOpsMixin(MCPMixin):
|
class PowerOpsMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class ResourcesMixin(MCPMixin):
|
class ResourcesMixin(MCPMixin):
|
||||||
@ -10,7 +10,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class SerialPortMixin(MCPMixin):
|
class SerialPortMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class SnapshotsMixin(MCPMixin):
|
class SnapshotsMixin(MCPMixin):
|
||||||
@ -8,7 +8,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class VCenterOpsMixin(MCPMixin):
|
class VCenterOpsMixin(MCPMixin):
|
||||||
@ -7,7 +7,7 @@ from mcp.types import ToolAnnotations
|
|||||||
from pyVmomi import vim
|
from pyVmomi import vim
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from esxi_mcp_server.connection import VMwareConnection
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
|
||||||
|
|
||||||
class VMLifecycleMixin(MCPMixin):
|
class VMLifecycleMixin(MCPMixin):
|
||||||
140
src/mcvsphere/server.py
Normal file
140
src/mcvsphere/server.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
"""FastMCP server setup for mcvsphere."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from fastmcp import FastMCP
|
||||||
|
|
||||||
|
from mcvsphere.config import Settings, get_settings
|
||||||
|
from mcvsphere.connection import VMwareConnection
|
||||||
|
from mcvsphere.mixins import (
|
||||||
|
ConsoleMixin,
|
||||||
|
DiskManagementMixin,
|
||||||
|
GuestOpsMixin,
|
||||||
|
HostManagementMixin,
|
||||||
|
MonitoringMixin,
|
||||||
|
NICManagementMixin,
|
||||||
|
OVFManagementMixin,
|
||||||
|
PowerOpsMixin,
|
||||||
|
ResourcesMixin,
|
||||||
|
SerialPortMixin,
|
||||||
|
SnapshotsMixin,
|
||||||
|
VCenterOpsMixin,
|
||||||
|
VMLifecycleMixin,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_server(settings: Settings | None = None) -> FastMCP:
|
||||||
|
"""Create and configure the FastMCP server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
settings: Optional settings instance. If not provided, will load from
|
||||||
|
environment variables and/or config file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Configured FastMCP server instance with VMware tools registered.
|
||||||
|
"""
|
||||||
|
if settings is None:
|
||||||
|
settings = get_settings()
|
||||||
|
|
||||||
|
# Configure logging - MUST go to stderr for stdio transport compatibility
|
||||||
|
log_level = getattr(logging, settings.log_level.upper(), logging.INFO)
|
||||||
|
|
||||||
|
# For stdio mode, suppress most logging to avoid interference
|
||||||
|
if settings.mcp_transport == "stdio":
|
||||||
|
log_level = logging.WARNING
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||||
|
stream=sys.stderr, # Explicitly use stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create FastMCP server
|
||||||
|
mcp = FastMCP(
|
||||||
|
name="mcvsphere",
|
||||||
|
instructions=(
|
||||||
|
"Model Control for vSphere - AI-driven VMware virtual machine management. "
|
||||||
|
"Provides tools for VM lifecycle management, power operations, "
|
||||||
|
"snapshots, guest OS operations, monitoring, and infrastructure resources."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create shared VMware connection
|
||||||
|
logger.info("Connecting to VMware vCenter/ESXi...")
|
||||||
|
conn = VMwareConnection(settings)
|
||||||
|
|
||||||
|
# Create and register all mixins
|
||||||
|
mixins = [
|
||||||
|
VMLifecycleMixin(conn),
|
||||||
|
PowerOpsMixin(conn),
|
||||||
|
SnapshotsMixin(conn),
|
||||||
|
MonitoringMixin(conn),
|
||||||
|
GuestOpsMixin(conn),
|
||||||
|
ResourcesMixin(conn),
|
||||||
|
DiskManagementMixin(conn),
|
||||||
|
NICManagementMixin(conn),
|
||||||
|
OVFManagementMixin(conn),
|
||||||
|
HostManagementMixin(conn),
|
||||||
|
VCenterOpsMixin(conn),
|
||||||
|
ConsoleMixin(conn),
|
||||||
|
SerialPortMixin(conn),
|
||||||
|
]
|
||||||
|
|
||||||
|
tool_count = 0
|
||||||
|
resource_count = 0
|
||||||
|
|
||||||
|
for mixin in mixins:
|
||||||
|
mixin.register_all(mcp)
|
||||||
|
tool_count += len(getattr(mixin, "_mcp_tools", []))
|
||||||
|
resource_count += len(getattr(mixin, "_mcp_resources", []))
|
||||||
|
|
||||||
|
# Get actual counts from MCP server
|
||||||
|
actual_tools = len(mcp._tool_manager._tools)
|
||||||
|
actual_resources = len(mcp._resource_manager._resources)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"mcvsphere ready - %d tools, %d resources registered",
|
||||||
|
actual_tools,
|
||||||
|
actual_resources,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mcp
|
||||||
|
|
||||||
|
|
||||||
|
def run_server(config_path: Path | None = None) -> None:
|
||||||
|
"""Run the mcvsphere server.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: Optional path to YAML/JSON config file.
|
||||||
|
"""
|
||||||
|
# Load settings
|
||||||
|
settings = Settings.from_yaml(config_path) if config_path else get_settings()
|
||||||
|
|
||||||
|
# Only print banner for SSE mode (stdio must stay clean for JSON-RPC)
|
||||||
|
if settings.mcp_transport == "sse":
|
||||||
|
try:
|
||||||
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
package_version = version("mcvsphere")
|
||||||
|
except Exception:
|
||||||
|
package_version = "dev"
|
||||||
|
|
||||||
|
print(f"mcvsphere v{package_version}", file=sys.stderr)
|
||||||
|
print("─" * 40, file=sys.stderr)
|
||||||
|
print(
|
||||||
|
f"Starting SSE transport on {settings.mcp_host}:{settings.mcp_port}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create and run server
|
||||||
|
mcp = create_server(settings)
|
||||||
|
|
||||||
|
if settings.mcp_transport == "sse":
|
||||||
|
mcp.run(transport="sse", host=settings.mcp_host, port=settings.mcp_port)
|
||||||
|
else:
|
||||||
|
# stdio mode - suppress banner to keep stdout clean for JSON-RPC
|
||||||
|
mcp.run(show_banner=False)
|
||||||
@ -98,7 +98,7 @@ async def main():
|
|||||||
|
|
||||||
server_params = StdioServerParameters(
|
server_params = StdioServerParameters(
|
||||||
command="uv",
|
command="uv",
|
||||||
args=["run", "esxi-mcp-server"],
|
args=["run", "mcvsphere"],
|
||||||
env={
|
env={
|
||||||
**os.environ,
|
**os.environ,
|
||||||
"VCENTER_HOST": dotenv.get("VCENTER_HOST", os.environ.get("VCENTER_HOST", "")),
|
"VCENTER_HOST": dotenv.get("VCENTER_HOST", os.environ.get("VCENTER_HOST", "")),
|
||||||
|
|||||||
@ -113,7 +113,7 @@ async def main():
|
|||||||
|
|
||||||
server_params = StdioServerParameters(
|
server_params = StdioServerParameters(
|
||||||
command="uv",
|
command="uv",
|
||||||
args=["run", "esxi-mcp-server"],
|
args=["run", "mcvsphere"],
|
||||||
env={
|
env={
|
||||||
**os.environ,
|
**os.environ,
|
||||||
"VCENTER_HOST": dotenv.get("VCENTER_HOST", ""),
|
"VCENTER_HOST": dotenv.get("VCENTER_HOST", ""),
|
||||||
|
|||||||
@ -102,7 +102,7 @@ async def main():
|
|||||||
|
|
||||||
server_params = StdioServerParameters(
|
server_params = StdioServerParameters(
|
||||||
command="uv",
|
command="uv",
|
||||||
args=["run", "esxi-mcp-server"],
|
args=["run", "mcvsphere"],
|
||||||
env={
|
env={
|
||||||
**os.environ,
|
**os.environ,
|
||||||
"VCENTER_HOST": dotenv.get("VCENTER_HOST", ""),
|
"VCENTER_HOST": dotenv.get("VCENTER_HOST", ""),
|
||||||
|
|||||||
64
uv.lock
generated
64
uv.lock
generated
@ -389,38 +389,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
{ url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "esxi-mcp-server"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = { editable = "." }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "fastmcp" },
|
|
||||||
{ name = "pydantic" },
|
|
||||||
{ name = "pydantic-settings" },
|
|
||||||
{ name = "pyvmomi" },
|
|
||||||
{ name = "pyyaml" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.optional-dependencies]
|
|
||||||
dev = [
|
|
||||||
{ name = "pytest" },
|
|
||||||
{ name = "pytest-asyncio" },
|
|
||||||
{ name = "ruff" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.metadata]
|
|
||||||
requires-dist = [
|
|
||||||
{ name = "fastmcp", specifier = ">=2.14.1" },
|
|
||||||
{ name = "pydantic", specifier = ">=2.0" },
|
|
||||||
{ name = "pydantic-settings", specifier = ">=2.0" },
|
|
||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
|
||||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
|
|
||||||
{ name = "pyvmomi", specifier = ">=8.0" },
|
|
||||||
{ name = "pyyaml", specifier = ">=6.0" },
|
|
||||||
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8" },
|
|
||||||
]
|
|
||||||
provides-extras = ["dev"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exceptiongroup"
|
name = "exceptiongroup"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -759,6 +727,38 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e2/fc/6dc7659c2ae5ddf280477011f4213a74f806862856b796ef08f028e664bf/mcp-1.25.0-py3-none-any.whl", hash = "sha256:b37c38144a666add0862614cc79ec276e97d72aa8ca26d622818d4e278b9721a", size = 233076, upload-time = "2025-12-19T10:19:55.416Z" },
|
{ url = "https://files.pythonhosted.org/packages/e2/fc/6dc7659c2ae5ddf280477011f4213a74f806862856b796ef08f028e664bf/mcp-1.25.0-py3-none-any.whl", hash = "sha256:b37c38144a666add0862614cc79ec276e97d72aa8ca26d622818d4e278b9721a", size = 233076, upload-time = "2025-12-19T10:19:55.416Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mcvsphere"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = { editable = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "fastmcp" },
|
||||||
|
{ name = "pydantic" },
|
||||||
|
{ name = "pydantic-settings" },
|
||||||
|
{ name = "pyvmomi" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "pytest-asyncio" },
|
||||||
|
{ name = "ruff" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "fastmcp", specifier = ">=2.14.1" },
|
||||||
|
{ name = "pydantic", specifier = ">=2.0" },
|
||||||
|
{ name = "pydantic-settings", specifier = ">=2.0" },
|
||||||
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
||||||
|
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24" },
|
||||||
|
{ name = "pyvmomi", specifier = ">=8.0" },
|
||||||
|
{ name = "pyyaml", specifier = ">=6.0" },
|
||||||
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8" },
|
||||||
|
]
|
||||||
|
provides-extras = ["dev"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdurl"
|
name = "mdurl"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user