Skip to main content
Glama

Vultr MCP

by rsp2k
block_storage.pyโ€ข13.2 kB
""" Vultr Block Storage FastMCP Module. This module contains FastMCP tools and resources for managing Vultr block storage volumes. """ from typing import Any, List from typing import Any, List from fastmcp import FastMCP def create_block_storage_mcp(vultr_client) -> FastMCP: """ Create a FastMCP instance for Vultr block storage management. Args: vultr_client: VultrDNSServer instance Returns: Configured FastMCP instance with block storage management tools """ mcp = FastMCP(name="vultr-block-storage") # Helper function to check if string is UUID format def is_uuid_format(value: str) -> bool: """Check if a string looks like a UUID.""" import re uuid_pattern = r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" return bool(re.match(uuid_pattern, value, re.IGNORECASE)) # Helper function to get block storage ID from label or ID async def get_block_storage_id(identifier: str) -> str: """ Get the block storage ID from label or existing ID. Args: identifier: Block storage label or ID Returns: The block storage ID Raises: ValueError: If the block storage volume is not found """ # If it looks like a UUID, return as-is if is_uuid_format(identifier): return identifier # Search by label volumes = await vultr_client.list_block_storage() for volume in volumes: if volume.get("label") == identifier: return volume["id"] raise ValueError(f"Block storage volume '{identifier}' not found") # Block Storage resources @mcp.resource("block-storage://list") async def list_volumes_resource() -> List[dict[str, Any]]: """List all block storage volumes.""" return await vultr_client.list_block_storage() @mcp.resource("block-storage://{volume_identifier}") async def get_volume_resource(volume_identifier: str) -> dict[str, Any]: """Get details of a specific block storage volume. Args: volume_identifier: The volume label or ID """ volume_id = await get_block_storage_id(volume_identifier) return await vultr_client.get_block_storage(volume_id) # Block Storage tools @mcp.tool async def list() -> List[dict[str, Any]]: """List all block storage volumes in your account. Returns: List of block storage volume objects with details including: - id: Volume ID - label: User-defined label - region: Region where volume is located - size_gb: Storage size in GB - status: Current status (active, pending, etc.) - attached_to_instance: Instance ID if attached (null if detached) - cost_per_month: Monthly cost - date_created: Creation date """ return await vultr_client.list_block_storage() @mcp.tool async def get(volume_identifier: str) -> dict[str, Any]: """Get detailed information about a specific block storage volume. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID to retrieve Returns: Detailed volume information including status, attachment, and cost """ volume_id = await get_block_storage_id(volume_identifier) return await vultr_client.get_block_storage(volume_id) @mcp.tool async def create( region: str, size_gb: int, label: str | None = None, block_type: str | None = None, ) -> dict[str, Any]: """Create a new block storage volume. Args: region: Region code where the volume will be created (e.g., "ewr", "lax", "fra") size_gb: Size in GB (10-40000 depending on block_type) label: Optional label for the volume (recommended for easy identification) block_type: Optional block storage type (affects size limits and performance) Returns: Created volume information including ID, cost, and configuration """ return await vultr_client.create_block_storage( region, size_gb, label, block_type ) @mcp.tool async def update( volume_identifier: str, size_gb: int | None = None, label: str | None = None, ) -> dict[str, str]: """Update block storage volume configuration. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID to update size_gb: New size in GB (can only increase, not decrease) label: New label for the volume Returns: Success confirmation """ volume_id = await get_block_storage_id(volume_identifier) await vultr_client.update_block_storage(volume_id, size_gb, label) changes = [] if size_gb is not None: changes.append(f"size to {size_gb}GB") if label is not None: changes.append(f"label to '{label}'") return { "success": True, "message": f"Volume updated: {', '.join(changes) if changes else 'no changes'}", "volume_id": volume_id, } @mcp.tool async def delete(volume_identifier: str) -> dict[str, str]: """Delete a block storage volume. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID to delete Returns: Success confirmation """ volume_id = await get_block_storage_id(volume_identifier) await vultr_client.delete_block_storage(volume_id) return { "success": True, "message": "Block storage volume deleted successfully", "volume_id": volume_id, } @mcp.tool async def attach( volume_identifier: str, instance_identifier: str, live: bool = True ) -> dict[str, str]: """Attach block storage volume to an instance. Smart identifier resolution: Use volume label/ID and instance label/hostname/ID. Args: volume_identifier: Volume label or ID to attach instance_identifier: Instance label, hostname, or ID to attach to live: Whether to attach without rebooting the instance (default: True) Returns: Success confirmation """ volume_id = await get_block_storage_id(volume_identifier) # Get instance ID using the instances module pattern if is_uuid_format(instance_identifier): instance_id = instance_identifier else: instances = await vultr_client.list_instances() instance_id = None for instance in instances: if ( instance.get("label") == instance_identifier or instance.get("hostname") == instance_identifier ): instance_id = instance["id"] break if not instance_id: raise ValueError(f"Instance '{instance_identifier}' not found") await vultr_client.attach_block_storage(volume_id, instance_id, live) return { "success": True, "message": f"Volume attached to instance {'without reboot' if live else 'with reboot'}", "volume_id": volume_id, "instance_id": instance_id, } @mcp.tool async def detach(volume_identifier: str, live: bool = True) -> dict[str, str]: """Detach block storage volume from its instance. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID to detach live: Whether to detach without rebooting the instance (default: True) Returns: Success confirmation """ volume_id = await get_block_storage_id(volume_identifier) await vultr_client.detach_block_storage(volume_id, live) return { "success": True, "message": f"Volume detached {'without reboot' if live else 'with reboot'}", "volume_id": volume_id, } @mcp.tool async def list_by_region(region: str) -> List[dict[str, Any]]: """List block storage volumes in a specific region. Args: region: Region code to filter by (e.g., "ewr", "lax", "fra") Returns: List of volumes in the specified region """ volumes = await vultr_client.list_block_storage() return [volume for volume in volumes if volume.get("region") == region] @mcp.tool async def list_unattached() -> List[dict[str, Any]]: """List all unattached block storage volumes. Returns: List of volumes that are not currently attached to any instance """ volumes = await vultr_client.list_block_storage() return [ volume for volume in volumes if volume.get("attached_to_instance") is None ] @mcp.tool async def list_attached() -> List[dict[str, Any]]: """List all attached block storage volumes with instance information. Returns: List of volumes that are currently attached to instances """ volumes = await vultr_client.list_block_storage() return [ volume for volume in volumes if volume.get("attached_to_instance") is not None ] @mcp.tool async def get_volume_status(volume_identifier: str) -> dict[str, Any]: """Get comprehensive status information for a block storage volume. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID Returns: Detailed status including attachment, usage, and cost information """ volume_id = await get_block_storage_id(volume_identifier) volume = await vultr_client.get_block_storage(volume_id) # Enhanced status information status_info = { **volume, "is_attached": volume.get("attached_to_instance") is not None, "attachment_status": "attached" if volume.get("attached_to_instance") else "detached", "size_info": { "current_gb": volume.get("size_gb", 0), "can_expand": True, # Block storage can always be expanded "max_size_gb": 40000, # Current Vultr limit }, "cost_info": { "monthly_cost": volume.get("cost_per_month", 0), "yearly_cost": (volume.get("cost_per_month", 0) * 12), }, } return status_info @mcp.tool async def get_mounting_instructions(volume_identifier: str) -> dict[str, Any]: """Get instructions for mounting a block storage volume on Linux. Smart identifier resolution: Use volume label or ID. Args: volume_identifier: Volume label or ID Returns: Step-by-step mounting instructions and commands """ volume_id = await get_block_storage_id(volume_identifier) volume = await vultr_client.get_block_storage(volume_id) # Generate mounting instructions device_name = "/dev/vdb" # Common device name for second block device mount_point = f"/mnt/{volume.get('label', 'block-storage')}" instructions = { "volume_info": { "id": volume_id, "label": volume.get("label", "unlabeled"), "size_gb": volume.get("size_gb", 0), "attached": volume.get("attached_to_instance") is not None, }, "prerequisites": [ "Volume must be attached to an instance", "Run commands as root or with sudo", "Backup any existing data before formatting", ], "commands": { "check_device": f"lsblk | grep {device_name[5:]}", "format_ext4": f"mkfs.ext4 {device_name}", "create_mount_point": f"mkdir -p {mount_point}", "mount_volume": f"mount {device_name} {mount_point}", "verify_mount": f"df -h {mount_point}", "auto_mount": f"echo '{device_name} {mount_point} ext4 defaults 0 0' >> /etc/fstab", }, "full_script": f"""# Complete mounting script for {volume.get("label", "block-storage")} sudo lsblk | grep {device_name[5:]} sudo mkfs.ext4 {device_name} sudo mkdir -p {mount_point} sudo mount {device_name} {mount_point} sudo df -h {mount_point} echo '{device_name} {mount_point} ext4 defaults 0 0' | sudo tee -a /etc/fstab""", } if not volume.get("attached_to_instance"): instructions["warning"] = ( "Volume is not attached to any instance. Attach it first before mounting." ) return instructions return mcp

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rsp2k/mcp-vultr'

If you have feedback or need assistance with the MCP directory API, please join our Discord server