Skip to content

v0.5.2 — MCP gateway fix + toolchain refresh + supply-chain monitoring

Choose a tag to compare

@Caua-ferraz Caua-ferraz released this 02 Jun 00:23
· 9 commits to master since this release

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:

  1. 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 /tmp behind agentguard-mcp-gateway. The current published version of that filesystem-server enforces the MCP spec strictly: params.capabilities is required on initialize. The gateway shipped through v0.5.1 declared the field omitempty in 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 with expected object, received undefined. Anyone following the README quickstart in the last several weeks hit this. v0.5.2 fixes it.
  2. 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.
  3. 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 of null (strict servers also reject null).

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@latest pulls v0.5.2.
  • Docker: docker pull against 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.0 for 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 --upstream workarounds that pinned an older filesystem-server.

Known issues

  • pkg/policy watcher tests have residual timing flake on Windows under heavy parallel go test ./... load. os.Rename semantics 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.