Packaging passmcp¶
Written for distribution maintainers. Everything a packager needs to decide whether passmcp fits their archive, and to build it without asking.
Maintainer contact: sebastian.rousseau@gmail.com · issues at https://github.com/sebastienrousseau/passmcp/issues.
What it is¶
passmcp: a CLI that connects to a remote Model Context Protocol server
with the credentials an operator was given, runs a nine-phase diagnostic
(network, OAuth discovery, credentials, handshake, protocol conformance,
catalog, safe execution, performance, resilience) and writes a scored
report with every request's telemetry. One static binary, no runtime
data files beyond the manpages and completions.
Licence¶
GPL-3.0-only, SPDX-identified. LICENSE at the repository root is the
full text, and every source file carries an SPDX header, verified in CI by
make spdx-check, so the tree is machine-readable for REUSE-style tooling.
Dependencies are all permissive (Apache-2.0, BSD-3-Clause, MIT): cobra, pflag, go-isatty, mousetrap and golang.org/x/sys. A CycloneDX SBOM is attached to every release archive.
Minimum toolchain policy¶
The floor is the go directive in go.mod. It is deliberately not
restated anywhere else, so it cannot disagree with itself; CI sets
GOTOOLCHAIN=auto and lets go.mod decide.
When it may rise: on any release, if a standard-library fix or language
feature is worth it. passmcp makes no distro-LTS compatibility promise. If
your archive pins an older Go, check go.mod before packaging a new
version rather than assuming the floor held.
The reason for each rise is recorded in the CHANGELOG entry for the release that raises it.
Building¶
No code generation at build time, no vendored tree, no CGO.
make build # or: go build -trimpath ./cmd/passmcp
make DESTDIR=$PWD/stage PREFIX=/usr install
make install honours PREFIX (default /usr/local) and DESTDIR, and
produces:
$PREFIX/bin/passmcp
$PREFIX/share/man/man1/passmcp.1
$PREFIX/share/man/man1/passmcp-<subcommand>.1
$PREFIX/share/bash-completion/completions/passmcp
$PREFIX/share/zsh/site-functions/_passmcp
$PREFIX/share/fish/vendor_completions.d/passmcp.fish
$PREFIX/share/doc/passmcp/{README.md,CHANGELOG.md,LICENSE,SECURITY.md}
make uninstall removes exactly that set. Both are exercised in CI by
make install-smoke, which stages an install on a clean runner and asserts
the tree.
Manpages and completions are generated, not committed. make install
runs the generator; if you build without it, run make docs first. They
are rendered from the cobra command tree so they cannot drift from
--help.
Dependency pin model¶
go.mod and go.sum are committed and authoritative; CI builds with
-mod=readonly, the Go default, so a build that would need to change
either fails instead. Builds are reproducible:
-trimpath: no build-machine paths in the binarymod_timestamppinned to the commitCGO_ENABLED=0: static, no libc coupling
A rebuild of a release tag reproduces the published binary bit for bit.
This was checked for v0.0.4, linux/amd64, on 2026-09-30: a clean clone
checked out at the tag and cross-compiled on a macOS arm64 machine with
Go 1.26.8 produced a binary identical to the one in the published
passmcp_Linux_x86_64.tar.gz (SHA-256 fa0ef416…). Other targets and
releases have not been compared; please report one that does not match.
To reproduce it:
git clone https://github.com/sebastienrousseau/passmcp.git
cd passmcp
git checkout v0.0.4
GOTOOLCHAIN=go1.26.8 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath \
-ldflags "-s -w -X satellion.com/passmcp/cmd.Version=0.0.4" \
-o passmcp ./cmd/passmcp
gh release download v0.0.4 -R sebastienrousseau/passmcp \
-p passmcp_Linux_x86_64.tar.gz -O - | tar -xzO passmcp > released
cmp passmcp released && sha256sum passmcp released
Three inputs have to match the release, and the release workflow sets all three:
- The Go toolchain, which is the
godirective ingo.modat the tag (go version -m releasednames it). - The flags:
-trimpathand the ldflags in.goreleaser.yaml, with the version without itsv. - The git checkout. The binary embeds the commit, its time and the
tag as the module version. A build from a source tarball, or with
-buildvcs=false, embeds none of that and differs.
Offline builds¶
Vendor the dependencies once, then build with no network:
go mod vendor
go build -mod=vendor -trimpath ./cmd/passmcp
go test -mod=vendor ./...
The test suite makes no network calls: every MCP and authorization
server it talks to is an httptest server in the same process. It is safe
in a sealed build environment.
Runtime dependencies¶
None. A CA bundle is needed to verify TLS on the servers passmcp tests,
which every distribution's ca-certificates package provides.
No daemon, no system user, no configuration file required to run. The
optional config file lives at $XDG_CONFIG_HOME/passmcp/config.json and
the token store passmcp login writes at $XDG_CONFIG_HOME/passmcp/tokens.json
with mode 0600.
Signature verification¶
Every release carries:
| Artefact | What it is |
|---|---|
checksums.txt |
SHA-256 of every asset |
checksums.txt.sigstore.json |
Keyless cosign signature (Sigstore bundle) |
checksums.txt.intoto.jsonl |
SLSA build provenance |
*.cdx.sbom.json |
CycloneDX SBOM per archive |
Verify a download:
$ cosign verify-blob checksums.txt \
--bundle checksums.txt.sigstore.json \
--certificate-identity-regexp 'https://github.com/sebastienrousseau/passmcp/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
$ sha256sum -c checksums.txt --ignore-missing
Or, with the GitHub CLI:
gh attestation verify passmcp_Linux_x86_64.tar.gz --owner sebastienrousseau
There is no long-lived signing key: signing is keyless via OIDC, so there
is no KEYS.asc to import and no key rotation for you to track.
Pre-built packages¶
The release pipeline publishes .deb and .rpm (via nfpm) and a
Homebrew cask. On Arch Linux, the AUR package passmcp builds from the
release's source; its PKGBUILD is a reference for anyone packaging
passmcp from source elsewhere. If you are packaging for an archive that
prefers to build from source, use make install above; the pre-built
packages exist for users, not to forestall distribution packaging.
What to watch when updating¶
- The version must have a heading in
CHANGELOG.md; the release workflow refuses a tag without one. - Manpage filenames follow the command tree. A new subcommand adds
passmcp-<name>.1; a glob (passmcp*.1) is safer than a fixed list. go.mod'sgodirective is the toolchain floor. Check it on every bump.