v0.5.2 — MCP gateway fix + toolchain refresh + supply-chain monitoring
AgentGuard 0.5.2 — Release Notes
Released: 2026-06-02
Headline: Maintenance release. Toolchain refresh (Go 1.22→1.25, Alpine 3.19→3.22, TS 5→6, Jest 29→30, Node floor 18→20 — every previous pin was past upstream EOL), supply-chain monitoring added (govulncheck on every push, SBOM SPDX + CycloneDX attached to every release), and a real bug fix in the MCP Gateway that restores the README's headline quickstart path against the current published @modelcontextprotocol/server-filesystem.
The self-hosted Apache-2.0 build remains fully featured. The hosted, multi-tenant version (AgentGuard Cloud) lives at https://agentguard.lictorate.com.
Why this release exists
Three independent reasons converged into one patch release:
- MCP Gateway quickstart was broken on the current upstream filesystem-server. The README's first integration path tells users to spin up
npx -y @modelcontextprotocol/server-filesystem /tmpbehindagentguard-mcp-gateway. The current published version of that filesystem-server enforces the MCP spec strictly:params.capabilitiesis required oninitialize. The gateway shipped through v0.5.1 declared the fieldomitemptyin its Go struct and normalised nothing at the send site, so when a host didn't pass capabilities the gateway forwarded the request with the field missing — and the upstream rejected it withexpected object, received undefined. Anyone following the README quickstart in the last several weeks hit this. v0.5.2 fixes it. - The full toolchain was overdue. Go 1.22 went EOL when 1.24 landed (only 1.25/1.26 are upstream-supported now). Alpine 3.19 was EOL before April 2026. Node 18 reached upstream EOL 2025-04-30. golangci-lint v1 is legacy; v2 is the only line getting fixes. None of these had to break v0.5.1 today, but they were all carrying credibility risk for the v1.0 push.
- Supply-chain hygiene is table stakes for the commercial build. The hosted AgentGuard Cloud effort needs to answer enterprise procurement: do you scan dependencies for CVEs, do you produce SBOMs, do you pin by hash. v0.5.2 adds the first two as automated workflows; the third is already provided by Go modules +
go.sum.
A second motivator: a maintainer access dispute around fsnotify surfaced in May 2026. v1.10.1 itself is legitimate (signed-off checksum pinned in go.sum), but the situation underscored that detection layering — dependabot + govulncheck + SBOM — is the right response to a transitive-dep supply-chain risk, not pre-emptively yanking the package.
Headline fix
MCP Gateway initialize is now spec-compliant
pkg/mcpgw/protocol.go and pkg/mcpgw/transport.go. The InitializeParams struct's Capabilities field was tagged json:"capabilities,omitempty", which meant a nil map silently dropped the field. The MCP spec requires capabilities to be present and an object. Strict upstreams (current @modelcontextprotocol/server-filesystem and any other spec-conformant strict server) reject the missing-field initialize with a validation error visible only in the upstream subprocess's stderr — opaque to anyone driving the gateway through a host like Claude Desktop or Cursor.
The fix is two surgical edits:
- The struct tag no longer has
omitempty— the field is always emitted. - The send site (
StdioUpstream.Initialize) normalises nil →map[string]interface{}{}so it serialises as{}instead ofnull(strict servers also rejectnull).
Verified end-to-end against the real npx -y @modelcontextprotocol/server-filesystem upstream: handshake, tools/list (returns the filesystem-server's 14-tool inventory with the gateway's fs: namespace prefix), and tools/call (policy-gated against the central server, denied per default policy with proper synthetic-refusal back to the client).
Other fixes
Policy watcher modTime race
pkg/policy/watcher.go. reload() updated w.modTime before attempting to parse. When a parse failed, the modTime was still advanced — effectively consuming a future mtime. A subsequent good save whose mtime happened to fall before the advanced value was silently skipped. The fsnotify v1.10.1 bump delivers events faster, which exposed this race reliably in TestWatchFile_BadYAMLKeepsWatcherAlive on Linux CI. modTime now advances only on successful parse.
test_guardedtool_batch_all_allowed order assertion
plugins/python/tests/test_langchain.py. The test asserted calls == ["a","b","c"] — execution order of a concurrent batch(). It passed by luck on Python 3.10–3.12's async scheduler and failed on 3.13. The output list out is still asserted exactly (LangChain preserves input/output alignment); only the calls invocation-order list is now sorted before comparison.
Toolchain & dependency refresh
| Was | Now | Why | |
|---|---|---|---|
| Go toolchain | 1.22 | 1.25 | 1.22 EOL when 1.24 landed; 1.25/1.26 are the only upstream-supported lines |
| Docker runtime base | alpine:3.19 |
alpine:3.22 |
3.19 EOL'd well before April 2026; 3.22 is supported through 2027-05 |
Node floor (engines.node) |
>=18.0.0 |
>=20.0.0 |
Node 18 EOL 2025-04-30 |
| TS SDK CI matrix | [18,20,22] |
[20,22,24] |
Drop EOL'd 18; add 24 (current Active LTS) |
| TypeScript | ^5.3.0 |
^6.0.0 |
Required "types": ["node", "jest"] in tsconfig (TS 6 stopped auto-discovering @types) |
| Jest | ^29.7.0 |
^30.0.0 |
Required dropping the two-generic jest.fn<Ret,Args>(…) signature at 9 call sites |
ts-jest |
^29.1.0 |
^29.4.0 |
No v30 exists; 29.4.x supports Jest 30 via peer deps |
@types/jest |
^29.5.0 |
^30.0.0 |
matches the Jest major |
@types/node |
^20.0.0 |
^22.0.0 |
matches a supported Node LTS |
| Python CI matrix | [3.10,3.11,3.12] |
[3.10,3.11,3.12,3.13] |
Trove classifier already claimed 3.13; CI now exercises it |
| golangci-lint | action@v6, v1.61.0 | action@v8, v2.12.2 | v1 line is legacy; v2 is the only one getting fixes |
github.com/fsnotify/fsnotify |
v1.9.0 | v1.10.1 | Refresh via go get -u && go mod tidy |
golang.org/x/sys (indirect) |
v0.13.0 | v0.45.0 | Same |
gopkg.in/yaml.v3 stays at v3.0.1 (canonical latest).
Supply-chain monitoring (new)
vulncheck CI job
Blocking. govulncheck runs on every push and pull request. Reachability-aware: only fires when a known-vulnerable symbol is actually called from project code, not when it merely appears in go.sum. This keeps the gate quiet on theoretical issues in unused transitive code while still catching the cases that matter. Defined in .github/workflows/ci.yml.
SBOM on every release
New workflow at .github/workflows/release-sbom.yml. Triggers on release: published. Builds the agentguard binary with the same flags as the Dockerfile, runs syft against the compiled artefact (not the source tree — scanning the binary records exactly what shipped, including the Go toolchain version), and attaches both formats:
agentguard-vX.Y.Z.spdx.json— SPDX is the ISO/IEC 5962 standard SOC 2 / FedRAMP auditors ask for by name.agentguard-vX.Y.Z.cdx.json— CycloneDX is what most security scanners (Dependency-Track, Grype, Snyk) ingest natively.
Producing both costs the workflow nothing extra and avoids "can you regenerate in X format" requests. v0.5.2 is the first release to exercise this workflow — confirm both assets are attached to the GitHub release before relying on it for v0.6.
.golangci.yml
First project-level golangci-lint config. Documents which patterns are intentionally excluded and why — so the next linter major bump (eventually v3) doesn't require re-discovering the rationale. errcheck off on _test.go files entirely; in production code, off only for conventional cleanup-path Close/Fprint receivers (stdlib io.Closer, *os.File, *sql.DB, *sql.Rows, *fsnotify.Watcher, and the project's audit Logger, policy.PolicyProvider, policy.Engine).
Breaking changes
TypeScript SDK consumers
If you embed the @agentguard/sdk source (rather than depending on the npm package), the floor moves to Node ≥ 20 and the build now requires TypeScript 6. Standard caret-pinned consumers ("@agentguard/sdk": "^0.5.2") are unaffected — the runtime behaviour and Guard API surface are identical.
MCP Gateway hosts on very old MCP clients
Almost certainly a non-event, but: if a client was relying on the gateway forwarding an initialize without capabilities to a permissive upstream, that request now always carries capabilities: {}. Any conforming upstream behaves identically; non-conforming upstreams that crashed on empty-object capabilities (we have never seen one) would now surface.
Nothing else
The Guard API, audit log schema, policy YAML schema, HTTP wire protocol, and CLI flag surface are all unchanged.
Upgrade notes
- Go installers (
go install): nothing to do;go install github.com/Caua-ferraz/AgentGuard/cmd/agentguard@latestpulls v0.5.2. - Docker:
docker pullagainst the next published image after this release. - Python SDK:
pip install --upgrade agentguardproxy. - TypeScript SDK:
npm install @agentguard/sdk@^0.5.2. If you were locked at^5.3.0for TypeScript in your project for any reason, the SDK now builds against TS 6 — but for consumers of the compiled package (the normal case), no action. - MCP Gateway operators on v0.5.1: the README's quickstart will now succeed against
npx -y @modelcontextprotocol/server-filesystem. No config change required; you may delete any--upstreamworkarounds that pinned an older filesystem-server.
Known issues
pkg/policywatcher tests have residual timing flake on Windows under heavy parallelgo test ./...load.os.Renamesemantics on Windows produce file-lock races that don't reflect Linux production behaviour. CI is Linux-only and unaffected; local-developer flake on Windows isn't a regression from v0.5.1.
Acknowledgements
This release is dependency hygiene and bug-fix work — no new external contributors. The MCP Gateway fix was discovered through end-to-end testing the README quickstart against the current published @modelcontextprotocol/server-filesystem; thanks to the MCP filesystem-server maintainers for tightening their initialize validation, which is what surfaced this.