Skip to content

Update stacklok/toolhive to v0.49.0 - #1150

Open
renovate[bot] wants to merge 4 commits into
mainfrom
renovate/stacklok-toolhive-0.x
Open

Update stacklok/toolhive to v0.49.0#1150
renovate[bot] wants to merge 4 commits into
mainfrom
renovate/stacklok-toolhive-0.x

Conversation

@renovate

@renovate renovate Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Update Change
stacklok/toolhive minor v0.48.0v0.49.0

After this PR opens, .github/workflows/upstream-release-docs.yml adds source-verified content edits for the new release. For stacklok/toolhive, the same workflow also syncs reference assets (CLI help, Swagger) and regenerates the CRD MDX pages.


Release Notes

stacklok/toolhive (stacklok/toolhive)

v0.49.0

Compare Source

🚀 Toolhive v0.49.0 is live!

A security- and auth-correctness release: a signer-pin bypass in thv skill upgrade is closed, the embedded auth server's documented zero-downtime key rotation finally works, and AWS STS role claims now fail closed instead of silently handing out the fallback role. This release also ships a dependency-light generated Go client for the management API, and moves the project to Go 1.27.

⚠️ Breaking Changes

  • pkg/vmcp/session.WithDialControl removed — vMCP embedders who set a dial-control hook on the session factory get a compile error; wrap the hook in the new WithDialControlResolver (migration guide below).
  • OAuth2 upstream token-endpoint auth method default reverted — only affects upgrades from v0.48.0: pre-registered oauth2 upstreams with a client secret and no explicit tokenEndpointAuthMethod go back to sending credentials in the POST body instead of HTTP Basic; set client_secret_basic explicitly if your IdP requires it (migration guide below).
  • AWS STS role claims must be a string or a list of strings — object, number, boolean, or null role claims now fail closed with HTTP 403 instead of silently receiving the fallback role, and a bare-string claim now selects its mapped role (migration guide below).
  • Root Go module now requires Go 1.27, and go:// workloads default to golang:1.27-alpine — builds pinned to Go 1.26 with GOTOOLCHAIN=local fail, and go:// servers that do not compile under Go 1.27 need an explicit image pin (migration guide below).
Migration guide: session.WithDialControlsession.WithDialControlResolver

Affects Go embedders of vMCP that called session.WithDialControl — the option added in v0.48.0 by #​6547. The option was address-blind, so every backend received the same net.Dialer.Control hook and a per-backend dial policy could not be expressed. It is replaced in place rather than deprecated alongside a second option.

On v0.49.0 the old call fails to compile with undefined: session.WithDialControl.

⚠️ pkg/vmcp/client.WithDialControl is unchanged. Only the pkg/vmcp/session option was renamed — do not migrate client.WithDialControl call sites.

Before
factory := session.NewSessionFactory(registry,
    session.WithDialControl(denyPrivateRanges),
)
After
factory := session.NewSessionFactory(registry,
    // Same hook for every backend — identical to v0.48.0 behavior.
    session.WithDialControlResolver(
        func(_ string) func(network, address string, c syscall.RawConn) error {
            return denyPrivateRanges
        },
    ),
)

Per-backend policy — the capability this unlocks. Returning nil for a workload leaves that backend on http.DefaultTransport, byte-for-byte identical to the no-hook path:

session.WithDialControlResolver(
    func(workloadID string) func(string, string, syscall.RawConn) error {
        if allowsPrivateDialing(workloadID) {
            return nil
        }
        return denyPrivateRanges
    },
)
Migration steps
  1. Find every session.WithDialControl( call site in the pkg/vmcp/session package — not pkg/vmcp/client, whose identically-named option is unchanged.
  2. Rename it to session.WithDialControlResolver.
  3. Wrap your existing hook in func(workloadID string) func(network, address string, c syscall.RawConn) error { return hook } to preserve v0.48.0 semantics exactly.
  4. Optionally branch on workloadID to vary policy per backend; return nil to leave a backend untouched.
  5. Make sure your resolver is goroutine-safe — it is invoked concurrently from the per-backend session-init goroutines. A panicking resolver is recovered per backend and excludes only that backend.
  6. Rebuild. If you are enforcing SSRF/DNS-rebinding protection, confirm the returned hook still inspects address — deciding allow/deny from workloadID alone provides no network-level protection.

PR: #​6567

Migration guide: OAuth2 upstream tokenEndpointAuthMethod default

Affects anyone on v0.48.0 with a pure oauth2-type upstream provider that uses a pre-registered clientId plus a client secret and leaves tokenEndpointAuthMethod unset.

#​6543 (shipped in v0.48.0, and only in v0.48.0) added the token_endpoint_auth_method field, but also made an unset field silently default to client_secret_basic whenever a secret was configured — flipping every existing pre-registered upstream from POST-body credentials to HTTP Basic with no opt-in. v0.49.0 restores the historical default while keeping the new field.

The auth style is strict, not probing: an unset method sends credentials in the token-request POST body and does not retry with Basic. Against a Basic-only IdP the exchange fails with invalid_client — on both initial login and token refresh.

  • Upgrading from v0.47.x or earlier → no change; v0.49.0 matches what you already had.
  • On v0.48.0 with an IdP that required POST body → v0.48.0 broke you and v0.49.0 fixes it.
  • On v0.48.0 with a Basic-only IdP → you must now opt in explicitly.

OIDC-type upstreams and Dynamic Client Registration upstreams are unaffected.

Before
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPExternalAuthConfig
spec:
  type: embeddedAuthServer
  embeddedAuthServer:
    upstreamProviders:
      - name: my-idp
        type: oauth2
        oauth2Config:
          clientId: my-client
          clientSecretRef:
            name: idp-client-secret
            key: client-secret
          tokenEndpoint: https://idp.example.com/oauth2/token
          # unset -> v0.48.0 silently used client_secret_basic
After
        oauth2Config:
          clientId: my-client
          clientSecretRef:
            name: idp-client-secret
            key: client-secret
          tokenEndpoint: https://idp.example.com/oauth2/token
          tokenEndpointAuthMethod: client_secret_basic   # now required to get Basic

Raw auth-server run config:

upstreams:
  - name: my-idp
    type: oauth2
    oauth2_config:
      client_id: my-client
      client_secret_env_var: MY_IDP_CLIENT_SECRET
      token_endpoint: https://idp.example.com/oauth2/token
      token_endpoint_auth_method: client_secret_basic   # add this
Migration steps
  1. Confirm this applies: you are coming from v0.48.0 and use a pre-registered (non-DCR) oauth2 upstream with a client secret.
  2. Check your IdP's token_endpoint_auth_methods_supported in its discovery document, or its client registration. If only client_secret_basic is accepted, act.
  3. Set tokenEndpointAuthMethod: client_secret_basic on every affected upstreamProviders[].oauth2Config (spec.embeddedAuthServer.upstreamProviders[] for MCPExternalAuthConfig, spec.authServerConfig.upstreamProviders[] for VirtualMCPServer), or token_endpoint_auth_method under upstreams[].oauth2_config in a raw run config.
  4. Apply and restart the workload, then verify a full login and a token refresh — refresh uses the same auth style.
  5. If your IdP accepts either style, or requires the POST body, do nothing.

The CRD schema is unchanged apart from doc text, so there is no CRD upgrade ordering concern.

PR: #​6648

Migration guide: AWS STS role claim shapes now fail closed

Affects deployments using an awsSts external auth config with claim-based roleMappings. Matcher-expression-only configurations are unaffected.

Role mappings are evaluated with the CEL expression claim_value in claims[role_claim_key], and CEL's in only has list and map overloads. Two bugs followed: a string role claim raised a swallowed "no such overload" error and silently produced the fallback role even on an exact match, and an object role claim made in test map-key membership, matching spuriously. Both are now corrected, and unsupported shapes fail closed rather than quietly granting a role.

Two behavior changes, both deliberate:

  1. A bare-string role claim exactly equal to a configured claim now selects its mapped role instead of fallbackRoleArn. Strings that merely contain the value still do not match.
  2. A role claim that is an object, number, boolean, or null now fails closed — HTTP 403 Failed to determine IAM role from the aws_sts middleware, or a failed backend call with failed to select IAM role in vMCP outbound auth.

A missing role claim still falls back exactly as before.

Before
{ "sub": "user1", "groups": { "admins": true } }
{ "sub": "user2", "groups": 7 }
After
{ "sub": "user1", "groups": ["admins"] }
{ "sub": "user1", "groups": "admins" }
Migration steps
  1. Decode a representative token for each IdP feeding an awsSts config and inspect the claim named by awsSts.roleClaim (default groups).
  2. List of strings → no action, behavior unchanged.
  3. Bare string → no config change needed, but confirm the outcome is intended: those users now receive the mapped role rather than fallbackRoleArn. Verify the mapped role's IAM trust policy accepts these subjects and that its permissions suit that population.
  4. Object, number, boolean, or null → change the IdP claim mapping to emit a string or a JSON array of strings (in Keycloak, use a multivalued group/role mapper and flatten nested claims like realm_access.roles to a top-level key — roleClaim is a flat lookup, not a dot path). Alternatively point roleClaim at a correctly-shaped claim, or convert those mappings to matcher CEL expressions, which are evaluated against the raw claims and are unaffected.
  5. Before rolling out, watch for the new WARN lines role claim has unsupported shape, failing closed and claim-based role mapping evaluation failed, failing closed — they name the offending role_arn. Note that CEL expression evaluation failed, skipping mapping was promoted from Debug to Warn, so pre-existing matcher-expression bugs will now appear at default log level.
  6. In a mixed configuration, re-check priorities: a claim mapping with a lower priority number than a previously-winning matcher mapping now wins for string claims.

PR: #​6306 — Closes #​6305

Migration guide: Go 1.27 toolchain and go:// builder image

Two separate audiences.

go:// workload users. The default builder image for go:// workloads moved from golang:1.26-alpine to golang:1.27-alpine. Only freshly built go:// workloads with no override are affected. Go's compatibility promise makes a failure unlikely, but a server relying on a removed deprecated API will not compile.

Downstream Go importers of the root module. github.com/stacklok/toolhive now declares go 1.27.0 with no toolchain directive. Under the default GOTOOLCHAIN=auto Go downloads 1.27 transparently; under GOTOOLCHAIN=local, a pinned-toolchain CI, an air-gapped build, or a distro-packaged Go, the build fails hard with go: go.mod requires go >= 1.27. The nested github.com/stacklok/toolhive/sdk/go module deliberately keeps its go 1.26.0 floor and is not affected.

Before
# ~/.toolhive/config.yaml — previously relied on the golang:1.26-alpine default
runtime_configs: {}
After
# Pin the previous builder image persistently
runtime_configs:
  go:
    builder_image: "golang:1.26-alpine"
    additional_packages:
      - ca-certificates
      - git
Migration steps
  1. For a one-off go:// run, pin per invocation: thv run go://github.com/example/server --runtime-image golang:1.26-alpine.
  2. For a persistent pin, set runtime_configs.go.builder_image in ~/.toolhive/config.yaml as above. additional_packages replaces rather than appends to the built-in ["ca-certificates", "git"], so list them explicitly. Only the builder stage is customizable for Go workloads; the runtime stage is always alpine:3.23.
  3. If you import the root module, upgrade your toolchain to Go 1.27+, or keep GOTOOLCHAIN=auto and allow Go to fetch the toolchain on demand.
  4. If you only need the management API client, depend on github.com/stacklok/toolhive/sdk/go instead — it retains the go 1.26.0 floor.
  5. In GitHub Actions, point setup-go at the root go-version-file: go.mod rather than pinning a version.

PR: #​6639

🆕 New Features

  • A new github.com/stacklok/toolhive/sdk/go module provides a typed, generated client covering all 77 documented management API operations, with safe default timeout and response-size handling, without pulling in ToolHive's full application dependency graph (#​6637).
  • Cedar policies can now govern the MCP SEP-2640 Skills extension on direct-proxied servers: skills/get maps to Action::"get_skill" on the skill's exact URI, and skills/list responses are filtered to the skills the caller may get — previously both methods were refused outright by default-deny, and skills/list without a get_skill permit now returns an empty list instead of a 403 (#​6512).
  • thv ai-plugin push --key <cosign.key> is available again for publishers using automatic local server discovery, now that key-signed plugins can be verified at install time with thv ai-plugin install --public-key and pinned in toolhive.lock.yaml for later sync/upgrade; remote or manually configured API URLs must still sign keylessly (#​6528).
  • The embedded auth server and vMCP Redis session storage can now connect to an unauthenticated Redis/Valkey instance by omitting the ACL user configuration, logging a startup WARN that names the store so an unintended downgrade stays visible (#​6551).

🐛 Bug Fixes

  • Security: thv skill upgrade --allow-signer-change no longer doubles as unsigned consent — it previously succeeded against an unsigned candidate, silently dropping a signer-pinned skill's recorded identity and rewriting the lock entry as unsigned: true; both thv skill upgrade and thv ai-plugin upgrade now report failed [unsigned-rejected] and name the uninstall … --scope project then install … --scope project --allow-unsigned sequence that records the exception explicitly (#​6629).
  • The auth server's /.well-known/jwks.json now publishes configured fallback keys alongside the signing key (primary first, de-duplicated by kid), making the documented three-step zero-downtime signing-key rotation actually work instead of a hard cutover that invalidated every outstanding JWT (#​6638 — Closes #​6451).
  • Progress notifications that arrived just before a request's final response are no longer silently dropped in the Streamable HTTP proxy — queued notifications/progress frames are flushed to the SSE stream, in backend order, before the response closes it (#​6491 — Closes #​6349).
  • VirtualMCPServer Deployments using spec.podTemplateSpec no longer get a metadata.generation bump and a spurious DeploymentUpdated event on every statusReportingInterval tick, including the 30s default — pod-template drift detection was comparing user-merged label maps for exact equality (#​6377 — Fixes #​6340).
  • OAuth error responses from the embedded auth server now preserve Fosite's RFC 6749 error codes and hints (invalid_client, invalid_grant, …) where a wrapped error could previously degrade to a generic server_error (#​6639).

🧹 Misc

  • vMCP session dial control is now resolved per backend workload rather than through a single address-blind hook, so a deployment can enforce a different dial policy for each backend at session initialization (#​6567).
  • Fixed a missing miniredis import that broke typecheck — and therefore every test — in pkg/authserver/runner on main (#​6636).
  • Fixed the Go SDK verification job, which was installing Go 1.26 for root-module generator tooling that now requires 1.27, and refreshed the stale generated SDK artifacts (#​6645).

📦 Dependencies

Module Version
github.com/stacklok/toolhive-core v0.0.47

Also migrates all Redis call sites from the now-deprecated toolhive-core/redis compatibility facade to redisconn directly (#​6646).

👋 Welcome to our newest contributor: @​isaacgao4396 🎉

Full commit log

What's Changed

New Contributors

Full Changelog: stacklok/toolhive@v0.48.0...v0.49.0

🔗 Full changelog: stacklok/toolhive@v0.48.0...v0.49.0


Configuration

📅 Schedule: (in timezone America/New_York)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Never, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.


Docs update for toolhive v0.49.0

At a glance

Upstream stacklok/toolhive v0.48.0v0.49.0
Hand-written changes 2 commit(s)
Reference assets refreshed (separate commit)
Gaps 0
Owner @reyortiz3 - identified from merged release PR stacklok/toolhive#6649
Release contributors 4 review requested (see sidebar) · 4 not requested (no docs impact)
Action required Spot-check skill-authored prose for accuracy

Who does what

@reyortiz3 cut this release and owns this PR: review your own changes, chase the remaining approvals, and merge once they're in. You don't need to wait on a review from anyone listed as having no docs impact below.

Everyone with a review request: the target is a review and approval within 2 business days.

Summary of changes

No docs impact identified

4 contributor(s) had no-docs-impact commits in this release. No review was requested, and the workflow did not auto-notify them.

Review routing

GitHub could not request the upstream contributor directly, so review went to the human merger of each relevant upstream PR:

Run cost

Session Turns Cost (USD)
Generation 127 $16.0803
Editorial review 40 $3.0977
Total 167 $19.1780
How this PR was built

Two Claude Opus sessions run per release: a generation pass
(upstream-release-docs skill, 6 phases) followed by a fresh-
context editorial pass (docs-review). Prettier/ESLint
auto-fixes are applied after.

Auto-synced paths — do not hand-edit these in review:

  • static/api-specs/
  • docs/toolhive/reference/cli/ (toolhive only)
  • docs/toolhive/reference/crds/

If a "Gaps needing human context" section is present above,
each entry includes a paste-ready Helper prompt for local
Claude
a reviewer can use to resolve the gap.

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@renovate renovate Bot added the autogen-docs Pull requests generated by automated release workflows label Sep 11, 2026
@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs-website Ready Ready Preview Sep 11, 2026 11:19pm UTC

Request Review

@github-actions
github-actions Bot marked this pull request as draft September 11, 2026 22:59
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @renovate[bot]'s task in 13m 38s —— View job


Upstream release docs — toolhive v0.49.0

Done. Pushed a4b6710 with 5 doc edits verified against source at the release tag.

Doc edits applied

Verified end-to-end, no doc change needed

Build passed. Prettier and ESLint clean. SUMMARY.md and REVIEWERS.json written for the workflow to pick up.

  • Load skill and review inputs
  • Phase 1: Inventory upstream changes
  • Phase 2: Verify each PR against source at the tag
  • Phase 3: Map to doc surfaces
  • Phase 4: Apply edits
  • Phase 5: Format, lint, build, displacement/completeness verification
  • Phase 6: Write SUMMARY.md + REVIEWERS.json artifacts
    · Branch: renovate/stacklok-toolhive-0.x

- Restore client_secret_post as the default token_endpoint_auth_method
  for pre-registered OAuth 2.0 upstreams on both K8s and vMCP embedded
  auth server pages.
- Document AWS STS role-claim shape rule: string or list of strings,
  other shapes fail closed with a 403.
- Add Skill entity, get_skill action, and skills/list response
  filtering to the Cedar authorization policy reference.
- Remove the stale "plugin push is keyless-only" note and describe
  the restored --key cosign-key-pair signing for locally discovered
  ToolHive servers.
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @renovate[bot]'s task in 3m 59s —— View job


Docs review for v0.49.0 content changes

  • Read the 5 files modified by the previous commit
  • Verify claims against /tmp/tmp.t579lmplvV/upstream (OAuth2 default, AWS STS shape rule, Cedar skills mapping, ai-plugin push --key restoration)
  • Apply actionable editorial fixes
  • Run npm run prettier:fix and npm run eslint:fix (both clean)
  • Commit and push (661c620)

Summary

All technical claims verify against the upstream source:

  • pkg/authserver/config.go:1482 confirms empty TokenEndpointAuthMethod "means the historical POST-body default."
  • pkg/auth/awssts/role_mapper.go:122-138 implements the string-or-list-of-strings normalization; error strings and the role claim has unsupported shape, failing closed warn line match the docs verbatim.
  • pkg/authz/middleware.go:56-57 and pkg/authz/authorizers/cedar/core.go:1205 confirm the skills/get/skills/listAction::"get_skill" / Skill::"<skill_uri>" mapping.
  • cmd/thv/app/ai_plugin_push.go and pkg/api/v1/plugins.go:315 confirm --key requires the locally discovered ToolHive server and returns 403 otherwise.

Fixes applied

1. docs/toolhive/guides-cli/ai-plugins.mdx — replaced PR-jargon opener ("Consumers installing…") with a second-person imperative ("To install a key-pair-signed plugin project-scoped, pass…"). The style guide explicitly flags "consumers" as engineering shorthand that shouldn't leak into docs.

2. docs/toolhive/integrations/aws-sts.mdx — dropped "continues to" from "A missing role claim continues to fall back to fallbackRoleArn." The word implies "as opposed to before," which is changelog framing; docs describe current behavior.

Nothing else flagged

The v0.49.0 breaking-change admonitions on both embedded-auth-server pages carry only the upgrade delta and action, as the style guide allows for versioned admonitions. No factual conflicts with upstream, no other jargon leaks, and no em/en dashes.
· Branch: renovate/stacklok-toolhive-0.x · Commit: 661c620

- Rewrite ai-plugin install step in second person; drop 'consumers'
  engineering jargon.
- Drop 'continues to' in the AWS STS fallback sentence; describe
  current behavior only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autogen-docs Pull requests generated by automated release workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant