Skip to content

Ship a container image, and list on the MCP Registry - #1

Merged
craigjmidwinter merged 3 commits into
mainfrom
oci-image-and-mcp-registry
Jul 30, 2026
Merged

Ship a container image, and list on the MCP Registry#1
craigjmidwinter merged 3 commits into
mainfrom
oci-image-and-mcp-registry

Conversation

@craigjmidwinter

Copy link
Copy Markdown
Owner

Adds an OCI image to the release so mail-muncher can be listed in the official MCP Registry, plus the server.json that is that listing.

Why OCI and not MCPB

The registry indexes servers by distributable package: npm, PyPI, NuGet, cargo, OCI or MCPB. A GitHub release of Go binaries is none of them, so today there is no way to be listed.

MCPB looked like the closer fit — prebuilt binaries off a GitHub release, no toolchain on the user's machine. Two things ruled it out:

  • It is 1 server out of 1200 in the live registry. Effectively unused.
  • It has no multi-arch story. packages[] has no platform discriminator, so four os/arch bundles give a client no way to choose.

A container manifest resolves architecture itself, and 51 registry entries already take this route.

Alpine, deliberately

imap.password_cmd is mandatory on the IMAP path and internal/provider/imap runs it as /bin/sh -c. A scratch or distroless image cannot authenticate to IMAP at all — not less conveniently, it fails outright.

The cost is honest and documented: inside a container password_cmd reaches the container's environment (printenv IMAP_PASSWORD, cat /run/secrets/…), not your host password manager. README says so plainly, including that any dest: in the config has to be a path the container can see.

Verified, not assumed

A snapshot build caught a real bug first: dockers_v2 lays the context out as <os>/<arch>/<binary>, so a bare COPY mail-muncher fails. Fixed via buildx's TARGETOS/TARGETARCH.

After the fix:

  • builds linux/amd64 and linux/arm64
  • runs as non-root with $HOME resolving the default config path
  • carries io.modelcontextprotocol.server.name, which the registry matches against name in server.json
  • answers MCP initialize + tools/list over stdio with all five tools
  • goreleaser check, go test ./... and claude plugin validate all pass

Sequencing

server.json names 0.4.0, not the current 0.3.0 — the image it points at does not exist until a release builds it. Order is: merge, tag v0.4.0, then mcp-publisher publish.

CI gains QEMU because the arm64 layer genuinely executes apk add and adduser, and packages: write to push to GHCR.

The caveat under "As a Claude Code skill" told readers the skill only
walks the Gmail path and has not caught up with `provider: imap` or
`mail-muncher init`. Both halves stopped being true at 2944723, which
reordered the skill to lead with IMAP; `init` is documented across
SKILL.md and references/setup.md as well.

Left alone it was actively steering people away from the two-minute
route the rest of the README spends its opening arguing for.
The official MCP Registry indexes servers by distributable package and
accepts npm, PyPI, NuGet, cargo, OCI and MCPB. A GitHub release of Go
binaries is none of those, so mail-muncher could not be listed at all.

OCI is the path that fits. MCPB looked closer -- prebuilt binaries off
a GitHub release, no toolchain -- but it is one server out of 1200 in
the live registry, and it has no way to say which of four os/arch
artifacts a client should take. A container manifest resolves the
architecture itself, and 51 registry entries already go this way.

The image is Alpine rather than scratch or distroless, which is not a
size oversight: `imap.password_cmd` is mandatory on the IMAP path and
internal/provider/imap runs it as `/bin/sh -c`. Without a shell, IMAP
auth cannot work at all. The trade is real and README says so -- inside
a container `password_cmd` reaches the container's environment, not the
password manager on your host.

`dockers_v2` hands the Dockerfile a build context laid out as
<os>/<arch>/<binary>, so the COPY resolves through buildx's TARGETOS
and TARGETARCH rather than a bare path. CI grows QEMU because the
arm64 layer genuinely executes `apk add` and `adduser`.

The registry proves ownership by matching an
`io.modelcontextprotocol.server.name` annotation on the pushed manifest
against `name` in server.json, so those two strings move together.

server.json names 0.4.0, not the current 0.3.0: the image it points at
does not exist until a release builds it, so publishing to the registry
follows the tag rather than leading it.

Verified by a snapshot build: image builds for both platforms, runs as
non-root with $HOME resolving the default config path, carries the
annotation, and answers an MCP initialize + tools/list with all five
tools over stdio.
The release dry run builds the container image now, and the linux/arm64
half genuinely runs `apk add` and `adduser` on an amd64 runner. Without
binfmt registered that is "exec /bin/sh: exec format error", which is
how this job failed the moment the image landed.

release.yml got the same two steps when the image was added; this job
lives in ci.yml and was missed.
@craigjmidwinter
craigjmidwinter merged commit 0f320b7 into main Jul 30, 2026
2 checks passed
@craigjmidwinter
craigjmidwinter deleted the oci-image-and-mcp-registry branch July 30, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant