Skip to main content
Glama
dkruyt

Hetzner Cloud MCP Server

by dkruyt

create_server

Provision a new virtual server on Hetzner Cloud by specifying its name, type, operating system image, location, and SSH keys.

Instructions

Create a new server.

Creates a new server with the specified configuration.

Examples:
- Basic server: {"name": "web-server", "server_type": "cx11", "image": "ubuntu-22.04"}
- With SSH keys: {"name": "app-server", "server_type": "cx21", "image": "debian-11", "ssh_keys": [123, 456]}
- Custom location: {"name": "db-server", "server_type": "cx31", "image": "ubuntu-22.04", "location": "fsn1"}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Implementation Reference

  • The handler function decorated with @mcp.tool() that implements the create_server tool. It resolves server type, image, location, and SSH keys from names/IDs, creates the server via hcloud client, and returns server details and action info.
    def create_server(params: CreateServerParams) -> Dict[str, Any]:
        """
        Create a new server.
        
        Creates a new server with the specified configuration.
        
        Examples:
        - Basic server: {"name": "web-server", "server_type": "cx11", "image": "ubuntu-22.04"}
        - With SSH keys: {"name": "app-server", "server_type": "cx21", "image": "debian-11", "ssh_keys": [123, 456]}
        - Custom location: {"name": "db-server", "server_type": "cx31", "image": "ubuntu-22.04", "location": "fsn1"}
        """
        try:
            # Get the objects needed for the API call
            try:
                # Debug the objects
                server_types = client.server_types.get_all()
                images = client.images.get_all()
                locations = client.locations.get_all()
                
                # Print available options for debugging
                server_type_names = [st.name for st in server_types]
                image_names = [img.name for img in images]
                location_names = [loc.name for loc in locations]
                
                # Try to get objects by name
                server_type_obj = client.server_types.get_by_name(params.server_type)
                image_obj = client.images.get_by_name(params.image)
                location_obj = client.locations.get_by_name(params.location)
                
                # Check if objects were found
                if server_type_obj is None:
                    return {"error": f"Server type '{params.server_type}' not found. Available types: {server_type_names}"}
                if image_obj is None:
                    return {"error": f"Image '{params.image}' not found. Available images: {image_names}"}
                if location_obj is None:
                    return {"error": f"Location '{params.location}' not found. Available locations: {location_names}"}
                
                # Handle SSH keys if provided - convert IDs to objects or use names
                ssh_keys = []
                if params.ssh_keys:
                    for ssh_key in params.ssh_keys:
                        # If SSH key is an integer ID, get the object
                        if isinstance(ssh_key, int):
                            ssh_key_obj = client.ssh_keys.get_by_id(ssh_key)
                            if ssh_key_obj:
                                ssh_keys.append(ssh_key_obj)
                        # If SSH key is a string name, get the object
                        elif isinstance(ssh_key, str):
                            ssh_key_obj = client.ssh_keys.get_by_name(ssh_key)
                            if ssh_key_obj:
                                ssh_keys.append(ssh_key_obj)
                
                # Create server with objects instead of strings
                response = client.servers.create(
                    name=params.name,
                    server_type=server_type_obj,
                    image=image_obj,
                    location=location_obj,
                    ssh_keys=ssh_keys
                )
            except Exception as e:
                return {"error": f"Failed to create server: {str(e)}"}
            
            # Extract server and action information
            server = response.server
            action = response.action
            
            # Don't wait for the action to complete - the method doesn't exist
            return {
                "server": server_to_dict(server),
                "action": {
                    "id": action.id,
                    "status": action.status,
                    "command": action.command,
                    "progress": action.progress,
                    "error": action.error,
                    "started": action.started.isoformat() if action.started else None,
                    "finished": action.finished.isoformat() if action.finished else None,
                } if action else None,
                "root_password": response.root_password,  # Only provided when no SSH keys are used
            }
        except Exception as e:
            return {"error": f"Failed to create server: {str(e)}"}
  • Pydantic BaseModel defining the input schema for the create_server tool parameters.
    class CreateServerParams(BaseModel):
        name: str = Field(..., description="Name of the server")
        server_type: str = Field(..., description="Server type (e.g., cx11, cx21, etc.)")
        image: str = Field(..., description="Image name or ID (e.g., ubuntu-22.04, debian-11, etc.)")
        location: Optional[str] = Field("nbg1", description="Location (e.g., nbg1, fsn1, etc.)")
        ssh_keys: Optional[List[int]] = Field(None, description="List of SSH key IDs")
  • Helper function to convert Hetzner Server object to a dictionary, used in the response of create_server and other server tools.
    def server_to_dict(server: Server) -> Dict[str, Any]:
        """Convert a Server object to a dictionary with relevant information."""
        return {
            "id": server.id,
            "name": server.name,
            "status": server.status,
            "created": server.created.isoformat() if server.created else None,
            "server_type": server.server_type.name if server.server_type else None,
            "image": server.image.name if server.image else None,
            "datacenter": server.datacenter.name if server.datacenter else None,
            "location": server.datacenter.location.name if server.datacenter and server.datacenter.location else None,
            "public_net": {
                "ipv4": server.public_net.ipv4.ip if server.public_net and server.public_net.ipv4 else None,
                "ipv6": server.public_net.ipv6.ip if server.public_net and server.public_net.ipv6 else None,
            },
            "included_traffic": server.included_traffic,
            "outgoing_traffic": server.outgoing_traffic,
            "ingoing_traffic": server.ingoing_traffic,
            "backup_window": server.backup_window,
            "rescue_enabled": server.rescue_enabled,
            "locked": server.locked,
            "protection": {
                "delete": server.protection["delete"] if server.protection else False,
                "rebuild": server.protection["rebuild"] if server.protection else False,
            },
            "labels": server.labels,
            "volumes": [volume.id for volume in server.volumes] if server.volumes else [],
        }

Latest Blog Posts

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/dkruyt/mcp-hetzner'

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