Skip to main content
Glama

server_provision

Provision cloud servers on Hetzner, DigitalOcean, Vultr, or Linode with optional Coolify/Dokploy installation or bare VPS setup. Automates server creation and initial configuration.

Instructions

Provision a new server on a cloud provider. Default: Coolify auto-install via cloud-init. Pass mode:'bare' for a generic VPS without Coolify (installs UFW and runs system updates only). Requires provider API token as environment variable (HETZNER_TOKEN, DIGITALOCEAN_TOKEN, VULTR_TOKEN, LINODE_TOKEN). WARNING: Creates a billable cloud resource. Blocked when KASTELL_SAFE_MODE=true. Server takes 3-5 minutes to fully initialize after provisioning.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
providerYesCloud provider to create server on
regionNoRegion/location ID (e.g. 'nbg1' for Hetzner, 'fra1' for DigitalOcean, 'ewr' for Vultr, 'us-east' for Linode). Uses template defaults if omitted
sizeNoServer type/plan ID (e.g. 'cax11' for Hetzner, 's-2vcpu-2gb' for DigitalOcean). Uses template defaults if omitted
nameYesServer hostname, 3-63 chars, lowercase, starts with letter, only alphanumeric and hyphens, ends with letter or number
templateNoTemplate for default region/size. 'starter' = cheapest, 'production' = more resources, 'dev' = development. Explicit region/size override template defaults. Default: starterstarter
modeNoServer mode: 'coolify' installs Coolify, 'dokploy' installs Dokploy, 'bare' provisions generic VPS. Default: coolifycoolify

Implementation Reference

  • The handleServerProvision function executes the provisioning logic, checking for SAFE_MODE, calling the core provisioner, and returning successful or error responses for the MCP tool.
    export async function handleServerProvision(params: {
      provider: SupportedProvider;
      region?: string;
      size?: string;
      name: string;
      template?: "starter" | "production" | "dev";
      mode?: "coolify" | "dokploy" | "bare";
    }, mcpServer?: McpServer): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
      const mode = params.mode ?? "coolify";
    
      // SAFE_MODE guard
      if (isSafeMode()) {
        return mcpError(
          "Provision is disabled in SAFE_MODE",
          "Set KASTELL_SAFE_MODE=false to enable server provisioning. WARNING: This creates billable cloud resources.",
        );
      }
    
      await mcpLog(mcpServer, `Provisioning ${params.provider} server: ${params.name}`);
    
      try {
        const result = await provisionServer({
          provider: params.provider,
          region: params.region,
          size: params.size,
          name: params.name,
          template: params.template,
          mode,
        });
    
        if (!result.success) {
          return mcpError(
            result.error ?? "Provision failed",
            result.hint,
            [
              {
                command: "server_info { action: 'list' }",
                reason: "Check existing servers",
              },
            ],
          );
        }
    
        if (!result.server) {
          return mcpError("Unexpected: server record missing");
        }
    
        const server = result.server;
    
        const suggestedActions =
          mode === "bare"
            ? [
                {
                  command: `ssh root@${server.ip}`,
                  reason: "Connect to your bare server via SSH",
                },
                {
                  command: `server_secure { action: 'secure-setup', server: '${server.name}' }`,
                  reason: "Harden SSH security + install fail2ban",
                },
                {
                  command: `server_secure { action: 'firewall-setup', server: '${server.name}' }`,
                  reason: "Setup UFW firewall",
                },
                {
                  command: `server_info { action: 'status', server: '${server.name}' }`,
                  reason: "Check cloud provider status",
                },
              ]
            : [
                {
                  command: `server_info { action: 'health', server: '${server.name}' }`,
                  reason: "Check Coolify health (wait 3-5 minutes after creation for Coolify to initialize)",
                },
                {
                  command: `server_secure { action: 'secure-setup', server: '${server.name}' }`,
                  reason: "Harden SSH security + install fail2ban",
                },
                {
                  command: `server_secure { action: 'firewall-setup', server: '${server.name}' }`,
                  reason: "Setup UFW firewall with Coolify ports",
                },
                {
                  command: `server_info { action: 'status', server: '${server.name}' }`,
                  reason: "Check cloud provider status",
                },
              ];
    
        await mcpLog(mcpServer, "Provision complete");
    
        return mcpSuccess({
          success: true,
          message: `Server "${server.name}" provisioned on ${server.provider}`,
          server: {
            id: server.id,
            name: server.name,
            provider: server.provider,
            ip: server.ip,
            region: server.region,
            size: server.size,
            mode,
            createdAt: server.createdAt,
          },
          ...(result.hint ? { hint: result.hint } : {}),
          suggested_actions: suggestedActions,
        });
      } catch (error: unknown) {
        return mcpError(
          error instanceof Error ? error.message : String(error),
        );
      }
    }
  • The serverProvisionSchema defines the input validation for the server_provision tool using Zod.
    export const serverProvisionSchema = {
      provider: z
        .enum(SUPPORTED_PROVIDERS)
        .describe("Cloud provider to create server on"),
      region: z
        .string()
        .optional()
        .describe(
          "Region/location ID (e.g. 'nbg1' for Hetzner, 'fra1' for DigitalOcean, 'ewr' for Vultr, 'us-east' for Linode). Uses template defaults if omitted",
        ),
      size: z
        .string()
        .optional()
        .describe(
          "Server type/plan ID (e.g. 'cax11' for Hetzner, 's-2vcpu-2gb' for DigitalOcean). Uses template defaults if omitted",
        ),
      name: z
        .string()
        .describe(
          "Server hostname, 3-63 chars, lowercase, starts with letter, only alphanumeric and hyphens, ends with letter or number",
        ),
      template: z
        .enum(["starter", "production", "dev"])
        .default("starter")
        .describe(
          "Template for default region/size. 'starter' = cheapest, 'production' = more resources, 'dev' = development. Explicit region/size override template defaults. Default: starter",
        ),
      mode: z
        .enum(["coolify", "dokploy", "bare"])
        .default("coolify")
        .describe(
          "Server mode: 'coolify' installs Coolify, 'dokploy' installs Dokploy, 'bare' provisions generic VPS. Default: coolify",
        ),
    };
  • Registration of the 'server_provision' tool within the MCP server setup.
    server.registerTool("server_provision", {
      description:
        "Provision a new server on a cloud provider. Default: Coolify auto-install via cloud-init. Pass mode:'bare' for a generic VPS without Coolify (installs UFW and runs system updates only). Requires provider API token as environment variable (HETZNER_TOKEN, DIGITALOCEAN_TOKEN, VULTR_TOKEN, LINODE_TOKEN). WARNING: Creates a billable cloud resource. Blocked when KASTELL_SAFE_MODE=true. Server takes 3-5 minutes to fully initialize after provisioning.",
      inputSchema: serverProvisionSchema,
      annotations: {
        title: "Server Provisioning",
        readOnlyHint: false,
        destructiveHint: true,
        idempotentHint: false,
        openWorldHint: true,
      },
    }, async (params) => {
      return handleServerProvision(params, server);

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/kastelldev/kastell'

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