Skip to main content
Glama

Dockerfile Check

CI npm downloads OpenSSF Scorecard License: MIT

Dockerfiles written by agents fail in two ways. Some never build: apt-get install without -y waits for an answer the build cannot give, COPY ../x reaches outside the context, a tag like node:18.99-alpine does not exist, COPY --from=dep names a stage called deps. Others build and ship a problem: Node.js 18 or Python 3.8 past their end of life, a secret in ENV, a container running as root, an image missing the arm64 build the servers need. Dockerfile Check finds both:

  • Syntax with the validator behind VS Code's Dockerfile support, and the rules a build and a review apply: install commands without -y, copies from outside the context, unknown or later stages, exec form in single quotes, invalid ports, legacy ENV, several CMDs.

  • Every base image against its registry: the tag exists (with the nearest real tags when it does not), its digest and a pinned FROM line, the platforms it is built for, when Docker Hub last rebuilt it. Docker Hub, GHCR, Quay, GCR, MCR, ECR Public and registry.k8s.io.

  • End of life of what the image is built on, runtime and OS alike (python:3.8-slim-buster is Python 3.8 and Debian 10), with an upgrade tag that exists (python:3.14-slim).

  • Security and size: root users, secrets in ENV and ARG, sudo, unverified ADD URLs, package caches left in layers, dependency installs placed after COPY . ..

Every finding has its line and fix. image_info answers the same questions for images named in compose files, Kubernetes manifests or CI jobs. No key needed.

Built and maintained by Arhan Canli.

Install

Install in Cursor Install in VS Code Install in Goose

Needs Node.js 20 or newer. No account or key.

Claude Code

claude mcp add dockerfile-check -- npx -y dockerfile-check-mcp

Claude Desktop: download dockerfile-check-mcp-<version>.mcpb from the latest release and open it. The bundle is signed; verify it with gh attestation verify <file> --repo arhancanli/dockerfile-check-mcp.

Any other client (Windsurf, Zed, Cline, Continue and others), in its MCP config file:

{
  "mcpServers": {
    "dockerfile-check": {
      "command": "npx",
      "args": [
        "-y",
        "dockerfile-check-mcp"
      ]
    }
  }
}

Docker

docker build -t dockerfile-check-mcp https://github.com/arhancanli/dockerfile-check-mcp.git && docker run -i --rm dockerfile-check-mcp

Hosted (Streamable HTTP): node src/server.mjs --http serves stateless MCP at POST /mcp (port from PORT, default 3000).

Related MCP server: Unbearable IaC Audit Pack

Example

An agent calls check_dockerfile with:

{
  "files": [
    {
      "path": "Dockerfile",
      "content": "ARG NODE_VERSION=18\nFROM node:${NODE_VERSION}-alpine AS deps\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\n\nFROM node:${NODE_VERSION}-alpine AS build\nWORKDIR /app\nCOPY --from=dep /app/node_modules ./node_modules\nCOPY . .\nRUN npm run build\n\nFROM python:3.8-slim-buster\nMAINTAINER ops@example.com\nARG GITHUB_TOKEN\nRUN apt-get update\nRUN apt-get install curl git\nCOPY ../shared/config.yml /etc/app/\nCOPY --from=build /app/dist /srv\nENV DATABASE_PASSWORD=hunter2\nEXPOSE 8080/http\nUSER root\nCMD ['python', '-m', 'http.server', '8080']\n"
    }
  ]
}

and gets back (recorded from the live server on 2026-09-27):

{
  "files": [
    {
      "path": "Dockerfile",
      "counts": {
        "error": 5,
        "warning": 9,
        "info": 2
      },
      "findings": [
        {
          "line": 9,
          "severity": "error",
          "rule": "unknown-stage",
          "message": "--from=dep names no earlier stage (stages: deps, build), so Docker pulls an image called dep.",
          "fix": "use a stage name defined above, or a full image reference"
        },
        {
          "line": 17,
          "severity": "error",
          "rule": "install-without-yes",
          "message": "apt-get install without -y waits for a confirmation the build cannot give, and the build fails.",
          "fix": "add -y (and --no-install-recommends)"
        },
        {
          "line": 18,
          "severity": "error",
          "rule": "outside-context",
          "message": "COPY cannot reach outside the build context (../shared/config.yml).",
          "fix": "move the file into the context, or build from a parent directory"
        },
        {
          "line": 21,
          "severity": "error",
          "rule": "invalid-port",
          "message": "EXPOSE 8080/http is not a port (1-65535, optionally /tcp or /udp)."
        },
        {
          "line": 23,
          "severity": "error",
          "rule": "exec-form-quotes",
          "message": "Exec form needs double quotes: with single quotes this is run as a shell command, brackets and all.",
          "fix": "write [\"cmd\", \"arg\"]"
        },
        {
          "line": 2,
          "severity": "warning",
          "rule": "end-of-life",
          "message": "node:18-alpine is built on Node.js 18, past its end of life (2025-04-30): no more security fixes.",
          "fix": "use node:24-alpine"
        },
        {
          "line": 7,
          "severity": "warning",
          "rule": "end-of-life",
          "message": "node:18-alpine is built on Node.js 18, past its end of life (2025-04-30): no more security fixes.",
          "fix": "use node:24-alpine"
        },
        {
          "line": 13,
... (125 more lines)

Tools

Tool

What it does

check_dockerfile

Checks Dockerfiles: syntax, what breaks the build (apt-get install without -y, copies outside the context, unknown stages), root users, secrets in ENV/ARG, cache order, and each base image against its registry (tag exists, digest to pin, platforms, last rebuild) and end-of-life dates. Findings have line and fix.

image_info

For container image references (node:20-alpine, ghcr.io/org/app:1.2): whether the tag exists (nearest real tags if not), its digest and a pinned reference, platforms, last rebuild (Docker Hub), and end-of-life status of its runtime and OS with an upgrade tag. Docker Hub, GHCR, Quay, GCR, MCR, ECR Public, registry.k8s.io.

How it behaves

  • Read-only: no tool changes anything outside this process. Dockerfiles never leave it; only image names are looked up.

  • Network: HTTPS only, to the hosts in package.json under factory.allowHosts, with a deadline, a size cap and bounded retries. Docker Hub is read through its tag API, which does not spend the anonymous pull allowance; the other registries through the OCI distribution API with anonymous tokens (registry.k8s.io through the regional Artifact Registry it redirects to). Private registries and private images are named as such and not contacted further. End-of-life dates come from endoflife.date. Registry answers are kept for 30 minutes, since tags move. Nothing is logged except unexpected failures (to stderr, without your inputs).

  • FROM lines are read as Docker reads them: ARG defaults declared before the first FROM are filled in, scratch and earlier stages are not images, and --platform on a FROM wins over the platform you pass.

  • A digest is the registry's Docker-Content-Digest, or the SHA-256 of the manifest when a registry does not send one (ECR Public).

  • Results are compact JSON with a matching output schema: errors first, then warnings, then notes.

Benchmark

Not yet measured.

Performance

Measured 2026-09-27 from Dubai, home connection against the live upstream, Node 24.19.0 (bench/perf.json, scripts/perf.mjs in the factory).

Call

First call

Repeat

Result size

check_dockerfile: a three-stage Dockerfile with twelve problems

1983 ms

1.3 ms

4,787 chars

check_dockerfile: a clean two-stage Node.js build

836 ms

1.6 ms

443 chars

check_dockerfile: an amd64-only base image built for linux/arm64

777 ms

0.8 ms

873 chars

image_info: nine references across seven registries

2286 ms

1.5 ms

3,174 chars

First call: a fresh server process, including the TLS connection and the upstream's own time. Repeat: the same call again, answered from the in-process cache, so it shows this server's own overhead.

Tool definitions the model reads on every turn (name, description, input schema): 1,429 characters. The full tool list, with the output schemas and annotations clients use to validate results, is 2,364 characters.

More MCP servers by Arhan Canli

  • Actions Check: Checks GitHub Actions workflows: outdated actions, old Node runtimes, retired runners, injection.

  • Config Check: Validates config files against their official schemas: tsconfig, compose, workflows, 1,400+ more.

  • Cron Check: Explains cron expressions, lists next run times in any time zone, converts between cron dialects.

  • Domain Health: Email and domain checks: SPF lookup limits, DKIM keys, DMARC, DNS records, registration expiry.

  • End of Life: Is this version still supported? EOL dates, latest patch and upgrade target for 470+ products.

  • Internet Standards: RFC sections, status, obsoleted-by chains, errata and IANA registries for coding agents.

  • Kube Check: Checks Kubernetes manifests for your version: removed APIs, unknown fields, Pod Security, risks.

  • License Check: Open source license answers: SPDX ids, copyleft, and whether a dependency's license fits yours.

  • The whole collection, 10 more

License

MIT, Copyright (c) 2026 Arhan Canli.

Available Tools

2 tools
check_dockerfileCheck DockerfilesA
Read-onlyIdempotent

Checks Dockerfiles: syntax, what breaks the build (apt-get install without -y, copies outside the context, unknown stages), root users, secrets in ENV/ARG, cache order, and each base image against its registry (tag exists, digest to pin, platforms, last rebuild) and end-of-life dates. Findings have line and fix.

ParametersJSON Schema
NameRequiredDescriptionDefault
filesYes
platformNothe platform you build for, e.g. linux/arm64

Output Schema

ParametersJSON Schema
NameRequiredDescription
filesYes

TDQS

A3.5/5.0
Behavior4/5

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

Annotations already declare readOnly=true, idempotent=true, destructive=false, openWorld=true, so the safety profile is covered. Beyond that the description usefully discloses the output shape ('Findings have line and fix'), which tells the agent how results are structured and how many checks it performs.

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?

A single dense sentence front-loads the verb and then lists check categories efficiently. Every clause maps to a real capability, though the run-on list of checks is slightly hard to parse.

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?

With an output schema present, return-value explanation is unnecessary, and annotations cover the safety profile. The description is complete about what is checked, missing only usage routing and any explanation of the required 'files' input.

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

Parameters2/5

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

Schema description coverage is only 50%: 'platform' is documented in the schema, but the required 'files' array (with its nested path/content objects, maxItems 20, and size limits) has no description anywhere. The description adds zero parameter guidance, so it fails to compensate for the undocumented required parameter.

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

Purpose4/5

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

The description opens with a specific verb+resource ('Checks Dockerfiles') and then enumerates concrete check categories — syntax, build breakers, root users, secrets, cache order, registry/EOL checks — so an agent knows exactly what the tool inspects. It does not, however, distinguish itself from the sibling image_info, leaving that differentiation to inference.

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

Usage Guidelines3/5

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

Usage is only implied by the scope of checks; there is no explicit statement of when to reach for this tool versus image_info or any precondition (e.g. that files must be supplied inline). The agent can guess the context but gets no routing guidance.

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

image_infoDoes this image tag exist, and is it supported?A
Read-onlyIdempotent

For container image references (node:20-alpine, ghcr.io/org/app:1.2): whether the tag exists (nearest real tags if not), its digest and a pinned reference, platforms, last rebuild (Docker Hub), and end-of-life status of its runtime and OS with an upgrade tag. Docker Hub, GHCR, Quay, GCR, MCR, ECR Public, registry.k8s.io.

ParametersJSON Schema
NameRequiredDescriptionDefault
imagesYes
platformNoreport whether each image is built for this platform, e.g. linux/arm64

Output Schema

ParametersJSON Schema
NameRequiredDescription
imagesYes

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), so the description's job is to add behavior. It does: fallback to nearest real tags when a tag is missing, digest and pinned reference output, last-rebuild limited to Docker Hub, EOL status of runtime and OS with an upgrade tag, and the explicit list of supported registries.

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?

It is a single dense sentence that leads with the scope and then lists outputs, so nothing is wasted. Slightly run-on and output-heavy, but it front-loads the most decision-relevant information.

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?

An output schema exists, so return values need not be explained, yet the description still usefully summarizes them. Combined with annotation coverage and the registry list, an agent has enough to call the tool correctly; only the missing when-to-use versus check_dockerfile is a gap.

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

Parameters3/5

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

Schema coverage is 50%: the platform parameter is documented in the schema, but images has no schema description. The description partially compensates by giving concrete image-reference examples (node:20-alpine, ghcr.io/org/app:1.2) that clarify the expected string format, so a baseline 3 is appropriate.

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?

The description names the exact resource (container image references) with concrete examples (node:20-alpine, ghcr.io/org/app:1.2) and enumerates what is answered: tag existence, digest, pinned reference, platforms, rebuild time, EOL status. It is clearly distinguishable from the sibling check_dockerfile, which operates on Dockerfiles rather than registry image references.

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

Usage Guidelines3/5

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

Usage is implied by the input shape: hand it an image reference and it reports whether the tag exists. However, there is no explicit when-to-use statement, no mention of the sibling check_dockerfile, and no guidance on when this is preferable to inspecting a Dockerfile or registry directly.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedcheck_dockerfile
    • First observedimage_info

TDQS

A3.7/5.0

Scored across 2 tools

Disambiguation4/5

check_dockerfile is clearly the Dockerfile linter while image_info is the image-reference inspector, so the core purposes are distinct. However, check_dockerfile also validates each base image against its registry (tag existence, digests, platforms, EOL), which overlaps with what image_info does, creating some potential for misselection on image-related queries.

Naming Consistency4/5

Both names use snake_case consistently, which is readable and predictable. The pattern is slightly uneven (verb_noun 'check_dockerfile' vs noun_noun 'image_info'), a minor deviation rather than a real inconsistency.

Tool Count3/5

Two tools is defensible for this narrow, focused domain (linting Dockerfiles plus inspecting image references). Still, it sits at the thin end and leaves little redundancy or granularity for users who only want one aspect checked.

Completeness4/5

The pair covers the stated purpose well: Dockerfile lint findings with lines and fixes, plus image tag/digest/platform/EOL data across major registries. Minor gaps remain, such as no autofix output or broader compose-file/context checks, but core workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides IaC auditing across Docker Compose, Dockerfile, and GitHub Actions with 64 checks, plus HU postcode validation, via a single MCP endpoint with pay-per-event billing.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Audits infrastructure configurations (Dockerfiles, GitHub Actions, Kubernetes, Terraform/Compose) against 14 deterministic security rules, returning BLOCK/REVIEW/PASS verdicts and signing attestations. Helps agents verify deployment configs before applying them.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables agents to review Terraform/Bicep infrastructure diffs against Checkov or builtin regex policies, returning structured findings for Azure security issues.
    Apache 2.0