Skip to main content
Glama

server_lock

Idempotent

Harden servers to production standards by applying 19 security steps including SSH key-only authentication, firewall configuration, system hardening, and integrity monitoring. Requires explicit confirmation for production deployment.

Instructions

Harden a server to production standard. Applies 19 hardening steps in a single SSH session: SSH key-only auth, fail2ban, UFW firewall, SSH cipher blacklist, sysctl hardening, unattended-upgrades, login banners, account locking, cloud metadata block, DNS security, APT validation, resource limits, service disabling, backup permissions, password quality policy, Docker daemon hardening (no-new-privileges, log rotation, live-restore, icc), auditd, log retention, and AIDE integrity. Requires production=true to confirm intent (safety gate). Pass dryRun=true to preview changes without applying. Platform-aware: preserves Coolify port 8000 or Dokploy port 3000 in UFW rules. Shows audit score before and after hardening. Requires SSH access to target server. For fine-grained SSH hardening, firewall port rules, or domain management, use server_secure instead.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
serverNoServer name or IP. Auto-selected if only one server exists.
productionNoSet to true to confirm hardening intent. Required to apply 19 hardening steps (safety gate). Omit or pass false to preview with dryRun=true.
dryRunNoPreview changes without applying. Returns what would be done. Bypasses the production safety gate.
forceNoForce lock even if server already appears hardened.

Implementation Reference

  • The handleServerLock function, which executes the server hardening process by applying 19 security steps via applyLock.
    export async function handleServerLock(params: {
      server?: string;
      production?: boolean;
      dryRun?: boolean;
      force?: boolean;
    }, mcpServer?: McpServer): Promise<McpResponse> {
      try {
        const servers = getServers();
        if (servers.length === 0) {
          return mcpError("No servers found", undefined, [
            { command: "kastell add", reason: "Add a server first" },
          ]);
        }
    
        const server = resolveServerForMcp(params, servers);
        if (!server) {
          if (params.server) {
            return mcpError(
              `Server not found: ${params.server}`,
              `Available servers: ${servers.map((s) => s.name).join(", ")}`,
            );
          }
          return mcpError(
            "Multiple servers found. Specify which server to use.",
            `Available: ${servers.map((s) => s.name).join(", ")}`,
          );
        }
    
        const production = params.production ?? false;
        const dryRun = params.dryRun ?? false;
        const force = params.force ?? false;
    
        // Safety gate: require explicit production=true unless doing a dry run
        if (!production && !dryRun) {
          return mcpError(
            "Pass production=true to confirm hardening intent. This applies 19 hardening steps in 4 groups: SSH & Auth (SSH config, fail2ban, login banners, account locking, SSH cipher blacklist), Firewall & Network (UFW, cloud metadata block, DNS security), System (sysctl, unattended-upgrades, APT validation, resource limits, service disabling, backup permissions, password quality, Docker hardening), Monitoring (auditd, log retention, AIDE integrity).",
            "Use dryRun=true to preview changes without applying.",
          );
        }
    
        // Resolve platform from server record (same pattern as serverAudit.ts line 44)
        const platformStr = server.platform ?? server.mode;
        const platform: Platform | undefined =
          platformStr === "coolify" || platformStr === "dokploy" ? platformStr : undefined;
    
        await mcpLog(mcpServer, `Starting 19-step hardening on ${server.name}`);
    
        const result = await applyLock(server.ip, server.name, platform, {
          production,
          dryRun,
          force,
        });
    
        if (!result.success) {
          return mcpError(result.error ?? "Lock hardening failed", result.hint);
        }
    
        await mcpLog(mcpServer, "Hardening complete");
    
        return mcpSuccess({
          success: result.success,
          steps: result.steps,
          ...(result.stepErrors && { stepErrors: result.stepErrors }),
          scoreBefore: result.scoreBefore,
          scoreAfter: result.scoreAfter,
        });
      } catch (error: unknown) {
        return mcpError(getErrorMessage(error));
      }
    }
  • The schema definition for the server_lock tool inputs, including production, dryRun, and force flags.
    export const serverLockSchema = {
      server: z.string().optional().describe("Server name or IP. Auto-selected if only one server exists."),
      production: z.boolean().default(false).describe("Set to true to confirm hardening intent. Required to apply 19 hardening steps (safety gate). Omit or pass false to preview with dryRun=true."),
      dryRun: z.boolean().default(false).describe("Preview changes without applying. Returns what would be done. Bypasses the production safety gate."),
      force: z.boolean().default(false).describe("Force lock even if server already appears hardened."),
    };
  • Registration of the server_lock tool within the MCP server setup.
    server.registerTool("server_lock", {
      description:
        "Harden a server to production standard. Applies 19 hardening steps in a single SSH session: SSH key-only auth, fail2ban, UFW firewall, SSH cipher blacklist, sysctl hardening, unattended-upgrades, login banners, account locking, cloud metadata block, DNS security, APT validation, resource limits, service disabling, backup permissions, password quality policy, Docker daemon hardening (no-new-privileges, log rotation, live-restore, icc), auditd, log retention, and AIDE integrity. Requires production=true to confirm intent (safety gate). Pass dryRun=true to preview changes without applying. Platform-aware: preserves Coolify port 8000 or Dokploy port 3000 in UFW rules. Shows audit score before and after hardening. Requires SSH access to target server. For fine-grained SSH hardening, firewall port rules, or domain management, use server_secure instead.",
      inputSchema: serverLockSchema,
      annotations: {
        title: "Server Lock",
        readOnlyHint: false,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: true,
      },
    }, async (params) => {
      return handleServerLock(params, server);
    });
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Massive value-add beyond annotations: enumerates all 19 hardening steps (SSH ciphers, fail2ban, AIDE, etc.), discloses platform-aware port preservation (Coolify/Dokploy), explains audit score reporting, and clarifies idempotent safety gate behavior. Annotations confirm non-read-only and non-destructive nature; description provides the implementation details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Information-dense and front-loaded: opens with summary, lists specific steps, covers safety mechanisms, and ends with alternatives. Length is justified by complexity (19 distinct operations), though slightly verbose for quick scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Comprehensive for a complex multi-step tool despite lacking output schema. Covers prerequisites (SSH), side effects (19 configurations), observability (audit scores), and platform exceptions. Minor gap: doesn't detail error handling or specific return structure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, establishing baseline 3. Description elevates this by adding semantic context: 'production=true' is framed as a 'safety gate', 'dryRun=true' explained as 'preview changes without applying', and platform-aware behavior contextualizes the 'server' parameter impact.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb ('Harden') + resource ('server') + scope ('production standard') with exhaustive list of 19 concrete steps. Explicitly distinguishes from sibling 'server_secure' at end: 'For fine-grained SSH hardening... use server_secure instead'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit safety gate ('Requires production=true to confirm intent'), preview mode instruction ('Pass dryRun=true to preview'), prerequisite disclosure ('Requires SSH access to target server'), and clear alternative routing to sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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