gum v2.1.0
This release fixes a wire bug that made every gum tool unavailable in strict MCP clients, implements the spec §8.7 remote plugin package sources, puts the spec §9.0 TOON document on the MCP response path, and makes installed plugin operations reachable from every surface.
Highlights
tools/listno longer emits"outputSchema": null. Strict MCP clients- Plugin manifests may declare
pypi,github_release, andgitpackage - MCP responses carry the spec §9.0 TOON wire document, and
gum gain - Installed plugin variants merge into the session catalog at process start,
- A profile now binds to one Google account. A login that returns a different
gum.codeenforces one cumulative output budget,gum_parallelenforces
reject null there, so in those clients v2.0.x exposed zero working tools. The field is now omitted when a tool has no output schema.
sources. Every remote source is pinned: a SHA-256 checksum selects the artifact, git sources pin a 40-hex commit, and unpinned or local installs are recorded as dev-untrusted in plugins.lock.
fixture replay measures it: 210 tokens saved (5.35%) across the release fixtures, up from 0 in v2.0.x.
with per-row validation, so search, describe, and dispatch reach plugin operations. In v2.0.x nothing read the plugin catalog back.
account, or a stored credential whose subject changes, is refused with AUTH_SUBJECT_MISMATCH. gum auth login --switch-account rebinds.
the §9.0.1 aggregate ceilings before dispatch, and capability-bearing scripts require CLI confirmation with capability-bound tokens.
Install
curl -fsSL https://raw.githubusercontent.com/ehmo/gum/main/install.sh | GUM_VERSION=v2.1.0 bash
gum --version
gum doctorUpgrade notes
- Users of strict MCP clients regain all 29 tools with no action beyond
- Each auth profile now records the Google account it was created with, and
- The dead
gum code --timeout-secflag is gone. The §6 wall-clock budget --format rawprints a notice naming the fields raw output drops. Pipelines
upgrading.
a login through that profile which comes back as a different account is refused before anything is stored. To rebind a profile on purpose, run gum auth login --switch-account. Dispatch refuses a resolved credential whose subject fingerprint differs from the recorded one with the same AUTH_SUBJECT_MISMATCH error and writes a credential_subject_changed audit event.
was already the only timeout; a script that passed the flag must drop it.
that parse stderr should ignore it or switch to --format json.
Added
- Remote plugin package sources (spec §8.7). A manifest
[package]block - Spec §9.0 TOON documents on the MCP response path, and `gum gain
gum gaintext and CSV renderers, gain modes, savings computed from the- Active plugin variants merge into the session catalog through one seam both
gum plugin list --format=json.- Curated
default_fieldsfor 11 high-traffic read operations, so the field_mask_mode="dual_fetch"issues the second, unmasked request and- Compound plugins receive a short-lived host Google access token under the
- A build-time first-party request schema store.
- The filesystem tee is wired into the shipped binary.
gum coderequires CLI confirmation for capability-bearing scripts, and agum auth login --switch-account.
declares source = "pypi" | "github_release" | "git" | "local" | "bundled". PyPI installs select the index artifact matching the declared sha256: checksum and build a host-managed virtualenv with pip install --no-index --no-deps; uvx and pipx are never spawned. GitHub release artifacts verify SHA-256 before a mode-pinned unpack that refuses symlinks, traversal, and non-regular entries. Git sources check out a pinned 40-hex commit and re-verify HEAD against the pin. plugins.lock rows record source, ref, and checksum; local and unpinned-git rows carry risk="dev-untrusted", which gum plugin list surfaces.
--fixture-replay` measures the real wire document.
ledger, and gain entries for failed dispatches.
the CLI and the MCP server use. A row missing its op, variant, owner, binding, adapter key, or tool name is refused, as is a binding that names another plugin's executable. On an op collision the generated catalog wins.
stage-1 field mask now has real data to inject.
reports both sides instead of being refused.
spec §7 forwarding rule.
confirmation token binds the script's capability flags.
Changed
- Each profile binds to one Google account (see Upgrade notes).
- Long-running operations are gated out of code mode.
gum.codeenforces one cumulative output budget across prints and the- JSON resource bodies are canonicalized per RFC 8785.
plugin-catalog.jsonis stamped with the install generation, so a torngen-catalogrefuses overrides without a manifest entry and enforces- grpc-sdk operations carry the
routing_headersinvariant.
return value; gum_parallel enforces the §9.0.1 aggregate ceilings before the first dispatch.
registry publish is detected instead of served.
DEFAULT_VARIANT_INVALID at generation time; dispatch falls through a quarantined default variant.
Fixed
tools/listemitted"outputSchema": nullfor every tool (see- Gain-ledger rotation no longer loses entries.
- An unparseable
meta_tools.search_apis.collapse_arrays.max_itemsvalue is gum://status/canariesreports the real canary roster.- Compound-auth failures report the real missing components, and Google
gum://status/healthbounds everydetailstring at 80 characters.
Highlights). A wire-level regression test now decodes the raw frame and fails if any tool puts a non-object outputSchema on the wire.
ignored instead of failing the request.
policy refusals map onto missing_components.
Removed
- The dead
gum code --timeout-secflag. - Two auth strategies the wire enum omits; they were undispatchable.
- The dead output-profile column from the convenience-tool table.
Security
- Remote plugin installs are checksum-pinned end to end; see Added. There is
- One dependency was added:
github.com/pelletier/go-toml/v2 v2.4.3. Only govulncheckreports the same result as v2.0.1: 0 vulnerabilities in code
no development escape hatch around the checksum gate.
the build-time gen-catalog tool imports it; the shipped gum binary does not link it.
this project calls, and one finding, GO-2026-5932 in module golang.org/x/crypto, which has no fixed version and no called symbol.
Known limitations
- 217 of 228 catalog variants still declare no
default_fields, so the §770 - No catalog variant declares a non-
fullexecution_support, so - The §10.2 HTTP and ETag cache backend has no production reader or writer.
- macOS binaries are not notarized. The Homebrew formula clears quarantine
- The
v2.0.0tag tree still failsmake fmt-check. A published tag is not
requirement that every variant carry them is still unmet outside the 11 curated read operations.
unsupported_capabilities and "partial" are declared and tested but not exercised by shipped data.
http.db and http-wal.db are created and never used.
during installation. For standalone installs, inspect with spctl --assess --type execute --verbose gum and use xattr -d com.apple.quarantine gum if Gatekeeper rejects the binary.
moved, because its binaries, checksums and provenance all name that commit.
Token savings
Measured with the release fixtures using a local build stamped 2.1.0, run from the apps/gum directory of the matching source checkout:
gum gain --fixture-replay --format=toon
gum gain --fixture-replay --format=json| Default format | Total calls | Total tokens in | Total tokens saved | Aggregate savings |
|---|---|---|---|---|
toon |
10 | 3,922 | 210 | 5.35 % |
json |
10 | 3,922 | -12 | 0.31 % overhead |
The toon row rose from 0 in v2.0.x because the replay now measures the §9.0 wire document instead of a shaping stage that preceded it.
Reproducibility
git clone https://github.com/ehmo/gum.git
cd gum && git checkout v2.1.0
cd apps/gum
GOTOOLCHAIN=go1.26.7 CGO_ENABLED=0 GOOS=<os> GOARCH=<arch> go build -trimpath \
-ldflags='-s -w -X main.version=2.1.0' ./cmd/gum
sha256sum gumBuild from a full clone, not from a linked git worktree. Go embeds the commit revision in the binary, and it silently skips that stamp in a linked worktree, which changes the hash.
Verification
Release run 35688916890, tag v2.1.0 at public commit 48bdc8bb086cde31a1de548dc3255a475295db73. All seven jobs passed: validate semver tag, docs deployed from tag commit, pre-release tests, govulncheck, goreleaser, reproducible-build canary, verify release provenance against release artifacts.
Checked independently of the pipeline, against the published artifacts:
shasum -a 256 -c checksums.txtreported OK for all four archives.- The provenance attestation matched: identity and archive hashes for
v2.1.0 - A clean
git clone --depth 1 --branch v2.1.0rebuilt all four binaries to the
at commit 48bdc8bb086cde31a1de548dc3255a475295db73.
hashes below, which equal release-binaries.sha256. The binaries extracted from the four published archives hash identically.
| Archive | sha256 |
|---|---|
gum_2.1.0_darwin_amd64.tar.gz |
ed77b3f45dde7bdfbb84addf82d45b83521966b8180ea119197cb66748f1c870 |
gum_2.1.0_darwin_arm64.tar.gz |
887e3ea381ad3aa1469ca491c60a397ef95d2a22bfcf95a35c95abcbfef4c7df |
gum_2.1.0_linux_amd64.tar.gz |
0aa33acd5c12812a599a141dfe420e524f1f0d1edd4b3777e96761825b8028cc |
gum_2.1.0_linux_arm64.tar.gz |
fb29f0256ebd01b085724ae8c4dda426abc74cfdf50489804549adf26d5dcada |
The Homebrew tap carries the same four digests at ehmo/homebrew-tap commit 28f9ad0. brew audit --strict --online --os=all --arch=all ehmo/tap/gum and brew test ehmo/tap/gum both passed, the tap-drift check passed, and the installed binary reports 2.1.0 with gum doctor: all checks passed.
