Skip to content

v0.32.0

Choose a tag to compare

@toolhive-release-app toolhive-release-app released this 30 Jun 16:43
ecb9a8d

πŸš€ Toolhive v0.32.0 is live!

This release advances vMCP cross-application authentication β€” adding the XAA (Cross-Application Access) outgoing-auth strategy, the OBO strategy seam, and upstream ID-token propagation β€” alongside meaningful security hardening (Origin/DNS-rebind protection and a credential-passthrough fix) and a batch of operator and proxy robustness fixes.

⚠️ Breaking Changes

  • Origin validation & SSE CORS hardening β€” ToolHive now validates the HTTP Origin header (MCP 2025-11-25 DNS-rebind protection) and removed the insecure Access-Control-Allow-Origin: * from the legacy SSE transport. Default local (loopback) usage is unaffected; only browser clients on a non-http://localhost origin need action via the new --allowed-origins flag (migration guide) (#4908).
  • vMCP Go embedders: server.New signature changed β€” pkg/vmcp/server.New dropped its discovery.Manager parameter (7 β†’ 6 args) and the pkg/vmcp/discovery package was removed. CLI, operator, and API behavior are unchanged; only out-of-tree code importing the vMCP library must update its call (migration guide) (#5627).
Migration guide: Origin validation & SSE CORS hardening

Who is affected: Only browser-based MCP clients that make cross-origin requests to ToolHive's SSE transport from an origin that is not http://localhost:<port> / http://127.0.0.1:<port> (for example, a web app served over HTTPS or from a custom hostname). Non-browser clients (IDEs, CLIs, MCP SDK clients) do not send an Origin header and are unaffected. Loopback binds (the default 127.0.0.1) auto-derive a matching local allowlist, so default local usage continues to work. Non-loopback binds without --allowed-origins are not enforced β€” a warning is logged instead.

Before

# Relied on the implicit Access-Control-Allow-Origin: * wildcard
thv run --transport sse some/mcp-server

After

# Explicitly allow the browser origin that needs cross-origin access
thv run --transport sse --allowed-origins https://my-web-app.example.com some/mcp-server

# The same flag is available on thv proxy:
thv proxy --allowed-origins https://my-web-app.example.com ...

Migration steps

  1. Determine whether any client reaches ToolHive's SSE endpoint cross-origin from a browser. If not, no action is needed.
  2. For each such origin, pass --allowed-origins=<scheme>://<host>:<port> on thv run / thv proxy. The flag is repeatable for multiple origins; matching is exact on scheme + host + port.
  3. Consider migrating browser clients off the legacy SSE transport to the streamable-HTTP transport.

PR: #4908

Migration guide: vMCP server.New signature change

Who is affected: Only out-of-tree Go code that imports github.com/stacklok/toolhive/pkg/vmcp/server and calls server.New directly (e.g. vMCP library embedders). All in-tree callers were updated in the same PR. There is no impact for CLI, operator, or API consumers.

Before

mgr := discovery.NewManager(agg)
srv, err := server.New(ctx, cfg, router, backendClient, mgr, backendRegistry, workflowDefs)

After

// discovery.Manager is removed; capability discovery is now the core's responsibility.
srv, err := server.New(ctx, cfg, router, backendClient, backendRegistry, workflowDefs)

Migration steps

  1. Remove the discovery.Manager argument from your server.New call (and delete the discovery.NewManager(...) construction).
  2. Ensure Config.Aggregator is set β€” the core now rejects a nil aggregator.
  3. If you set Config.AuthzMiddleware, also set Config.Authz; the combination without Authz now returns ErrInvalidConfig instead of silently allowing all requests.

PR: #5627

πŸ†• New Features

  • New XAA (Cross-Application Access) outgoing auth strategy implementing the ID-JAG two-step token exchange (RFC 8693 β†’ RFC 7523) for lazy per-backend cross-application access tokens (#5684).
  • Surface upstream ID tokens through the auth middleware, consolidating upstream credential retrieval into a single bulk lookup that carries both access and ID tokens (#5682).
  • Added the vMCP OBO (on-behalf-of) strategy seam β€” a new optional OBO field on BackendAuthStrategy plus strategy registration and override hook (#5624).
  • The vMCP optimizer can now use an OpenAI-compatible embedding client via the optional embeddingProvider/embeddingModel config fields (defaults to TEI, so existing configs are unchanged) (#5633).
  • Added insecureAllowHTTP to EmbeddedAuthServerConfig so VirtualMCPServer deployers can explicitly allow an http:// issuer for in-cluster hosts, with admission-time validation instead of a proxyrunner crash (#5671).
  • The operator Helm chart now prints a post-install NOTES.txt with verification commands, a minimal MCPServer example, and documentation links (#5656).

πŸ› Bug Fixes

  • vMCP now returns HTTP 401 + WWW-Authenticate (RFC 6750) when an upstream provider token is expired and cannot be refreshed, letting clients re-authenticate instead of receiving an opaque error (#5651).
  • Security: the upstreamswap custom header strategy no longer forwards the client's ToolHive JWT in Authorization to the backend; the upstream IdP token is still delivered in the configured custom header (#5661).
  • MCPRemoteProxy now mounts and validates the referenced OIDC CA bundle ConfigMap, fixing silent TLS failures and surfacing a CABundleRefValidated status condition (#5630).
  • Concurrent upstream-token refreshes are now deduplicated on a shared refresher, preventing spurious upstream logouts on IdPs with refresh-token rotation + reuse detection (#5635).
  • The operator no longer perpetually reconciles MCPServers using Redis session storage with a password ref β€” deploymentNeedsUpdate now mirrors the Redis password env var (#5639).
  • Container-internal target ports are no longer validated against host availability, so SSE/streamable-HTTP workloads keep their registry-defined target port (#5638).
  • Remote proxies now set X-Forwarded-Proto to the upstream scheme, fixing infinite redirect loops when MCPRemoteProxy runs behind a TLS-terminating load balancer (#5646).
  • Upstream-token refresh now fails closed when a rotated refresh token can't be persisted, deleting the stale row instead of stranding a poisoned token (#5636).
  • A typed-nil *Identity stored in the request context is now treated as absent, restoring vMCP fallback identity injection and preventing nil-deref panics (#5653).
  • VirtualMCPServer now always applies its PodTemplateSpec strategic merge patch, so fields like runtimeClassName, topologySpreadConstraints, and hostNetwork are no longer silently dropped (#5641).
  • RestoreSession no longer fabricates a partial identity, and session restore now threads the authenticated request context, fixing cross-pod Redis failover with upstream-auth strategies (#5650).
  • DetachedEnvVarValidator no longer rejects optional secret env vars left blank, fixing spurious "missing required secret" errors when installing registry servers via ToolHive Studio (#5689).

🧹 Misc

  • Landed Phase 2 of the plugin lifecycle epic β€” new pkg/plugins / pluginsvc packages, validation, and storage migration (no user-facing surface yet) (#5676).
  • Removed the now-unreachable legacy vMCP discovery seam and default router (#5627).
  • Removed redundant stale-ref scans from config controller watches, relying on controller-runtime's old+new object enqueue (βˆ’648 lines) (#5626).
  • Removed redundant annotation-based reconcile triggers in config controllers and added the missing MCPToolConfig watch (#5629).
  • Added unit + E2E test coverage for --allow-docker-gateway deny/allow behavior (#5644).
  • Added E2E test infrastructure for upstreamInject identity propagation after cross-pod Redis restore, plus several embedded-auth-server in-cluster fixes (#5660).
  • Expanded CONTRIBUTING guidelines with good-first-issue and good-tenth-issue descriptions (#5634).

πŸ“¦ Dependencies

Module Version
github.com/stacklok/toolhive-catalog v0.20260629.0
anthropics/claude-code-action v1.0.159 (digest a92e7c7)
actions/create-github-app-token v3.2.0
alpine (Docker tag) v3.24.1
Core workflow actions (updated)
Setup and language actions (updated)

πŸ‘‹ Welcome to our newest contributors: @syf2211 and @claude[bot] πŸŽ‰

Full commit log

What's Changed

  • Remove dead vMCP discovery seam and default router by @tgrunnagle in #5627
  • Remove redundant stale-ref scans from config controller watches by @ChrisJBurns in #5626
  • Add good-first-issue and good-tenth-issue contribution guidelines by @ChrisJBurns in #5634
  • Dedup upstream-token refresh on a shared refresher by @jhrozek in #5635
  • Removed redundant annotation-based reconcile triggers in config ctrls by @Sanskarzz in #5629
  • Mount and validate OIDC CA bundle on remote proxy by @ChrisJBurns in #5630
  • Add vMCP OBO strategy seam (OBOConfig field + stub + override hook) by @tgrunnagle in #5624
  • fix(operator): mirror Redis password env in deploymentNeedsUpdate by @syf2211 in #5639
  • fix(runner): do not validate container target port on host by @syf2211 in #5638
  • Add tests for --allow-docker-gateway behavior by @ChrisJBurns in #5644
  • fix(proxy): set X-Forwarded-Proto to upstream scheme for remote proxies by @claude[bot] in #5646
  • Fail closed on unpersisted rotated refresh token by @jhrozek in #5636
  • fix(auth): treat typed-nil Identity in context as absent by @syf2211 in #5653
  • fix(operator): always apply vMCP PodTemplateSpec strategic merge patch by @syf2211 in #5641
  • Return 401+WWW-Authenticate when vMCP upstream token is unrefreshable by @tgrunnagle in #5651
  • Add OpenAI-compatible embedding client to vmcp optimizer by @gabrielcosi in #5633
  • Update anthropics/claude-code-action digest to a92e7c7 by @renovate[bot] in #5662
  • Update core workflow actions by @renovate[bot] in #5663
  • Update setup and language actions by @renovate[bot] in #5664
  • Update anthropics/claude-code-action action to v1.0.159 by @renovate[bot] in #5665
  • feat(operator-chart): add post-install NOTES.txt (#4615) by @syf2211 in #5656
  • Expose InsecureAllowHTTP on EmbeddedAuthServerConfig for VirtualMCPServer by @tgrunnagle in #5671
  • Strip client Authorization on upstreamswap custom header by @jerm-dro in #5661
  • Add plugin core service, validation, and storage (Phase 2, THV-0077) by @JAORMX in #5676
  • Update actions/create-github-app-token action to v3.2.0 by @renovate[bot] in #5667
  • Update alpine Docker tag to v3.24.1 - autoclosed by @renovate[bot] in #5669
  • Add Origin header validation for DNS-rebind protection by @JAORMX in #4908
  • Update module github.com/stacklok/toolhive-catalog to v0.20260629.0 by @renovate[bot] in #5683
  • Surface upstream ID tokens through auth middleware by @jhrozek in #5682
  • Add XAA (Cross-Application Access) outgoing auth strategy by @jhrozek in #5684
  • Stop fabricating partial identity in RestoreSession by @tgrunnagle in #5650
  • Add E2E test for upstreamInject identity propagation after cross-pod Redis restore by @tgrunnagle in #5660
  • Fix DetachedEnvVarValidator rejecting optional secret env vars by @danbarr in #5689
  • Release v0.32.0 by @toolhive-release-app[bot] in #5692

New Contributors

Full Changelog: v0.31.0...v0.32.0

πŸ”— Full changelog: v0.31.0...v0.32.0