Skip to content

v0.20.0

Choose a tag to compare

@stacklokbot stacklokbot released this 14 Apr 16:29
· 51 commits to main since this release
7c3cdc7

🚀 Toolhive v0.20.0 is live!

This release graduates groupRef to a typed struct across all CRDs, adds stateless streamable-HTTP proxy support, brings TelemetryConfigRef to VirtualMCPServer, and expands the skills ecosystem with 10 new client integrations.

⚠️ Breaking Changes

  • GroupRef changed from bare string to typed struct — existing manifests with groupRef: my-group will fail CRD validation; update to groupRef: { name: my-group } and re-apply (migration guide)
  • protectedResourceAllowPrivateIP / jwksAllowPrivateIP field separation — users who set jwksAllowPrivateIP: true and relied on the (unintended) side effect of it also enabling private IPs for the protected resource endpoint must now set both fields explicitly (migration guide)
Migration guide: GroupRef

Affected resources: MCPServer, MCPRemoteProxy, MCPServerEntry, VirtualMCPServer — every user deploying these CRDs with a groupRef value.

Before

spec:
  groupRef: my-group

After

spec:
  groupRef:
    name: my-group

Migration steps

  1. Update all YAML manifests, GitOps pipelines, and Helm values to use the new struct format.
  2. For VirtualMCPServer, move spec.config.groupRef to spec.groupRef (the old path still works via ResolveGroupName() but is deprecated).
  3. Apply the new CRDs: kubectl apply -f deploy/charts/operator-crds/files/crds/
  4. Delete and re-create affected resources (existing etcd objects have the old string format and will fail re-validation):
    kubectl get mcpservers -A -o yaml > mcpservers-backup.yaml
    # Edit backup to use new format, then:
    kubectl delete mcpservers --all -A
    kubectl apply -f mcpservers-backup.yaml
    # Repeat for mcpremoteproxies, mcpserverentries, virtualmcpservers

PR: #4809 — Closes #4634

Migration guide: protectedResourceAllowPrivateIP

Affected users: Those who set jwksAllowPrivateIP: true and relied on it also enabling private IPs for the protected resource endpoint (due to a converter bug).

Before

spec:
  config:
    incomingAuth:
      oidc:
        jwksAllowPrivateIP: true
        # protectedResourceAllowPrivateIP was silently derived from jwksAllowPrivateIP

After

spec:
  config:
    incomingAuth:
      oidc:
        jwksAllowPrivateIP: true
        protectedResourceAllowPrivateIP: true  # must be set explicitly

Migration steps

  1. If your protected resource endpoint is on a private IP and you had only jwksAllowPrivateIP: true, add protectedResourceAllowPrivateIP: true to your OIDC config (inline, MCPOIDCConfig, or ConfigMap).
  2. No manifest format changes are required — existing YAML is structurally valid.
  3. The failure mode is immediate and obvious (connection refused), not silent degradation.

PR: #4784

🔄 Deprecations

  • spec.config.groupRef on VirtualMCPServer deprecated in favour of spec.groupRef (typed struct) — will be removed in a future release (#4809)
  • spec.config.telemetry on VirtualMCPServer deprecated in favour of spec.telemetryConfigRef, which enables Kubernetes-native secret references and per-server overrides — will be removed in a future release (#4801)

🆕 New Features

  • Stateless streamable-HTTP remote server support via --stateless flag — proxies MCP servers that only accept POST requests, returning spec-compliant 405 for GET/HEAD/DELETE (#4515)
  • Non-standard OAuth scope parameter support via --remote-auth-scope-param-name — enables authentication with providers like Slack that use user_scope instead of scope (#4712)
  • TelemetryConfigRef for VirtualMCPServer — reference shared MCPTelemetryConfig resources with CEL validation, config hash tracking, and CA bundle support, consistent with MCPServer and MCPRemoteProxy (#4801)
  • OpenAPI spec for v0.1 skills registry endpoints — browsing endpoints now documented at /api/openapi.json (#4800)
  • New MCP client support: Kimi Code CLI (#4788), Factory.ai (Droid) (#4795)
  • Skill-supporting clients: VS Code / VS Code Insiders (#4773), Goose / Gemini CLI / Amp (#4799), Kiro (#4803), Cline / Roo Code / Windsurf / Mistral Vibe / Trae / Antigravity (#4804)

🐛 Bug Fixes

  • protectedResourceAllowPrivateIP was silently ignored on VirtualMCPServer — the field is now propagated through the OIDC resolver and converter correctly (#4784)
  • Skill install failure when multiple clients share the same directory — deduplication helper prevents duplicate filesystem operations (#4796)
  • Health check timeout silently capped at 5s — removed redundant context.WithTimeout that overrode TOOLHIVE_HEALTH_CHECK_PING_TIMEOUT (#4791)
  • VirtualMCPServer rejected tokens from its own auth server when clients omitted the optional RFC 8707 resource parameter — now defaults to the sole allowed audience (#4805)

🔒 Security

  • Updated go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp from v1.41.0 to v1.43.0 to address CVE-2026-39882 — OTLP HTTP exporters read full response bodies without a size cap, enabling memory exhaustion (#4683)

🧹 Misc

  • Remove redundant storage.Load from NotifyBackendExpired (#4779)
  • Add regression guard for auth context propagation through Close() (#4781)
  • Add Audience to authserver test client fixture (#4814)
  • Isolate TestParseGitReference from ambient env (#4813)
  • Add ConfigMap content assertions to vMCP telemetry integration tests (#4807)
  • Add more Claude rules (#4782)

📦 Dependencies

Module Version
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0
helm/helm v3.20.2
thv-registry-api image v1.1.1

👋 Welcome to our newest contributor: @gmogmzGithub 🎉

Full commit log

What's Changed

  • Add Kimi Code CLI as a supported MCP client by @samuv in #4788
  • Support stateless streamable-HTTP remote servers by @gkatz2 in #4515
  • Remove redundant storage.Load from NotifyBackendExpired by @yrobla in #4779
  • Add more claude rules by @yrobla in #4782
  • Add VS Code and VS Code Insiders as skill-supporting clients by @samuv in #4773
  • Fix skill install failure when multiple clients share the same directory by @samuv in #4796
  • Add Factory.ai as a supported MCP client by @samuv in #4795
  • Add regression guard for auth context propagation through Close() by @yrobla in #4781
  • Propagate protectedResourceAllowPrivateIP through OIDC resolver and converter by @ChrisJBurns in #4784
  • Clean up health check dead code and timeout by @gkatz2 in #4791
  • Add Goose, Gemini CLI, and Amp as skill-supporting clients by @samuv in #4799
  • Add OpenAPI annotations to v0.1 skills registry endpoints by @samuv in #4800
  • Add Kiro as a skill-supporting client by @samuv in #4803
  • Update module go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp to v1.43.0 [SECURITY] by @renovate[bot] in #4683
  • Bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp from 1.41.0 to 1.43.0 by @dependabot[bot] in #4684
  • Update dependency helm/helm to v3.20.2 by @renovate[bot] in #4756
  • Add Cline, Roo Code, Windsurf, Mistral Vibe, Trae, and Antigravity as skill-supporting clients by @samuv in #4804
  • Add TelemetryConfigRef support to VirtualMCPServer CRD by @ChrisJBurns in #4801
  • Update registry API image to v1.1.1 by @rdimitrov in #4806
  • Grant default aud when resource is absent by @blkt in #4805
  • Add Audience to authserver test client fixture by @blkt in #4814
  • Isolate TestParseGitReference from ambient env by @blkt in #4813
  • Add ConfigMap content assertions to vMCP telemetry integration tests by @ChrisJBurns in #4807
  • Change GroupRef from bare string to typed MCPGroupRef struct by @ChrisJBurns in #4809
  • Add --remote-auth-scope-param-name for non-standard OAuth scope parameters by @gmogmzGithub in #4712
  • Release v0.20.0 by @stacklokbot in #4816

New Contributors

Full Changelog: v0.19.0...v0.20.0

🔗 Full changelog: v0.19.0...v0.20.0